diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e8d2896 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,65 @@ +# Git +.git +.gitignore + +# Documentation +README.md +CODE_OF_CONDUCT.md +LICENSE +*.md + +# Node.js +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE +.vscode +.idea +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# Docker +Dockerfile* +docker-compose* +.dockerignore diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml new file mode 100644 index 0000000..3fa0d82 --- /dev/null +++ b/.gitea/workflows/docker-build.yml @@ -0,0 +1,163 @@ +name: Build and Publish Docker Images + +on: + push: + branches: [ main, develop ] + tags: [ 'v*' ] + pull_request: + branches: [ main ] + +env: + REGISTRY: docker.io + IMAGE_NAME_DISCORD_BOT: uptime-kuma-discord-bot + IMAGE_NAME_WEB_BACKEND: uptime-kuma-web-backend + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata for Discord Bot + id: meta-discord-bot + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Extract metadata for Web Backend + id: meta-web-backend + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Discord Bot image + uses: docker/build-push-action@v5 + with: + context: ./Bot + file: ./Bot/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta-discord-bot.outputs.tags }} + labels: ${{ steps.meta-discord-bot.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build and push Web Backend image + uses: docker/build-push-action@v5 + with: + context: ./Web + file: ./Web/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta-web-backend.outputs.tags }} + labels: ${{ steps.meta-web-backend.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Generate SBOM for Discord Bot + if: github.event_name != 'pull_request' + uses: anchore/sbom-action@v0 + with: + image: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:${{ steps.meta-discord-bot.outputs.version }} + format: spdx-json + output-file: discord-bot-sbom.spdx.json + + - name: Generate SBOM for Web Backend + if: github.event_name != 'pull_request' + uses: anchore/sbom-action@v0 + with: + image: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:${{ steps.meta-web-backend.outputs.version }} + format: spdx-json + output-file: web-backend-sbom.spdx.json + + - name: Upload SBOM artifacts + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: sbom-artifacts + path: | + discord-bot-sbom.spdx.json + web-backend-sbom.spdx.json + retention-days: 30 + + security-scan: + runs-on: ubuntu-latest + needs: build-and-push + if: github.event_name != 'pull_request' + permissions: + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Trivy vulnerability scanner for Discord Bot + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest + format: 'sarif' + output: 'discord-bot-trivy-results.sarif' + + - name: Run Trivy vulnerability scanner for Web Backend + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest + format: 'sarif' + output: 'web-backend-trivy-results.sarif' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: | + discord-bot-trivy-results.sarif + web-backend-trivy-results.sarif + + notify: + runs-on: ubuntu-latest + needs: [build-and-push, security-scan] + if: always() && github.event_name != 'pull_request' + + steps: + - name: Notify on success + if: needs.build-and-push.result == 'success' && needs.security-scan.result == 'success' + run: | + echo "✅ Docker images built and published successfully!" + echo "Discord Bot: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest" + echo "Web Backend: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest" + + - name: Notify on failure + if: needs.build-and-push.result == 'failure' || needs.security-scan.result == 'failure' + run: | + echo "❌ Docker build or security scan failed!" + exit 1 diff --git a/.gitea/workflows/docker-compose-test.yml b/.gitea/workflows/docker-compose-test.yml new file mode 100644 index 0000000..1073d90 --- /dev/null +++ b/.gitea/workflows/docker-compose-test.yml @@ -0,0 +1,125 @@ +name: Test Docker Compose Setup + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +env: + COMPOSE_PROJECT_NAME: uptime-kuma-test + +jobs: + test-compose: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create test environment file + run: | + cat > .env << EOF + DISCORD_TOKEN=test_token_12345 + GUILD_ID=123456789012345678 + CHANNEL_ID=123456789012345678 + CLIENT_ID=123456789012345678 + UPDATE_TIME=60 + UPTIME_KUMA_URL=https://test.example.com/metrics + UPTIME_KUMA_API_KEY=test_api_key_12345 + EOF + + - name: Build Docker images + run: | + docker-compose build --no-cache + + - name: Start services + run: | + docker-compose up -d + sleep 30 + + - name: Check service health + run: | + # Check if web backend is responding + curl -f http://localhost:8080/back-end.php || exit 1 + + # Check container status + docker-compose ps + + # Check logs for errors + docker-compose logs --tail=50 + + - name: Test web backend endpoint + run: | + # Test the PHP endpoint (should fail gracefully with test data) + response=$(curl -s -w "%{http_code}" http://localhost:8080/back-end.php) + echo "Response: $response" + # Accept both 200 (success) and 500 (expected failure with test data) + if [[ "$response" =~ ^(200|500)$ ]]; then + echo "✅ Web backend endpoint is responding" + else + echo "❌ Web backend endpoint failed" + exit 1 + fi + + - name: Verify container logs + run: | + # Check Discord bot logs for startup + docker-compose logs discord-bot | grep -E "(Bot is online|Error)" || echo "No startup messages found" + + # Check web backend logs + docker-compose logs web-backend | grep -E "(Apache|PHP)" || echo "No backend messages found" + + - name: Cleanup + if: always() + run: | + docker-compose down -v + docker system prune -f + + lint-dockerfiles: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run hadolint on Dockerfiles + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Bot/Dockerfile + failure-threshold: warning + + - name: Run hadolint on Web Dockerfile + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Web/Dockerfile + failure-threshold: warning + + validate-compose: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate docker-compose.yml + run: | + docker-compose config --quiet + echo "✅ docker-compose.yml is valid" + + - name: Check for required environment variables + run: | + # Check if all required env vars are referenced in docker-compose.yml + required_vars=("DISCORD_TOKEN" "GUILD_ID" "CHANNEL_ID" "CLIENT_ID" "UPTIME_KUMA_URL" "UPTIME_KUMA_API_KEY") + + for var in "${required_vars[@]}"; do + if grep -q "\${$var}" docker-compose.yml; then + echo "✅ $var is referenced in docker-compose.yml" + else + echo "❌ $var is missing from docker-compose.yml" + exit 1 + fi + done diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..acba42c --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,94 @@ +name: Create Release + +on: + push: + tags: + - 'v*' + +env: + REGISTRY: docker.io + IMAGE_NAME_DISCORD_BOT: uptime-kuma-discord-bot + IMAGE_NAME_WEB_BACKEND: uptime-kuma-web-backend + +jobs: + create-release: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/} + VERSION=${VERSION#v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=v$VERSION" >> $GITHUB_OUTPUT + + - name: Generate changelog + id: changelog + run: | + # Get commits since last tag + if git describe --tags --abbrev=0 HEAD~1 >/dev/null 2>&1; then + PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1) + echo "Changes since $PREV_TAG:" + git log --pretty=format:"- %s (%h)" $PREV_TAG..HEAD + else + echo "Initial release" + git log --pretty=format:"- %s (%h)" + fi > CHANGELOG.md + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.version.outputs.tag }} + name: Release ${{ steps.version.outputs.tag }} + body: | + ## What's Changed + + ${{ steps.changelog.outputs.content }} + + ## Docker Images + + ### Discord Bot + ```bash + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:${{ steps.version.outputs.version }} + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest + ``` + + ### Web Backend + ```bash + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:${{ steps.version.outputs.version }} + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest + ``` + + ## Quick Start + + ```bash + # Clone the repository + git clone + cd UptimeKuma-DiscordBot + + # Copy environment template + cp env.example .env + + # Edit .env with your values + # Then start with Docker Compose + docker-compose up -d + ``` + + See [DOCKER_SETUP.md](DOCKER_SETUP.md) for detailed instructions. + files: | + CHANGELOG.md + draft: false + prerelease: false + + - name: Update Docker Hub description + if: success() + run: | + echo "🎉 Release ${{ steps.version.outputs.tag }} created successfully!" + echo "Docker images will be automatically built and pushed by the docker-build workflow." diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..3fa0d82 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,163 @@ +name: Build and Publish Docker Images + +on: + push: + branches: [ main, develop ] + tags: [ 'v*' ] + pull_request: + branches: [ main ] + +env: + REGISTRY: docker.io + IMAGE_NAME_DISCORD_BOT: uptime-kuma-discord-bot + IMAGE_NAME_WEB_BACKEND: uptime-kuma-web-backend + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata for Discord Bot + id: meta-discord-bot + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Extract metadata for Web Backend + id: meta-web-backend + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Discord Bot image + uses: docker/build-push-action@v5 + with: + context: ./Bot + file: ./Bot/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta-discord-bot.outputs.tags }} + labels: ${{ steps.meta-discord-bot.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build and push Web Backend image + uses: docker/build-push-action@v5 + with: + context: ./Web + file: ./Web/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta-web-backend.outputs.tags }} + labels: ${{ steps.meta-web-backend.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Generate SBOM for Discord Bot + if: github.event_name != 'pull_request' + uses: anchore/sbom-action@v0 + with: + image: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:${{ steps.meta-discord-bot.outputs.version }} + format: spdx-json + output-file: discord-bot-sbom.spdx.json + + - name: Generate SBOM for Web Backend + if: github.event_name != 'pull_request' + uses: anchore/sbom-action@v0 + with: + image: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:${{ steps.meta-web-backend.outputs.version }} + format: spdx-json + output-file: web-backend-sbom.spdx.json + + - name: Upload SBOM artifacts + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: sbom-artifacts + path: | + discord-bot-sbom.spdx.json + web-backend-sbom.spdx.json + retention-days: 30 + + security-scan: + runs-on: ubuntu-latest + needs: build-and-push + if: github.event_name != 'pull_request' + permissions: + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Trivy vulnerability scanner for Discord Bot + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest + format: 'sarif' + output: 'discord-bot-trivy-results.sarif' + + - name: Run Trivy vulnerability scanner for Web Backend + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest + format: 'sarif' + output: 'web-backend-trivy-results.sarif' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: | + discord-bot-trivy-results.sarif + web-backend-trivy-results.sarif + + notify: + runs-on: ubuntu-latest + needs: [build-and-push, security-scan] + if: always() && github.event_name != 'pull_request' + + steps: + - name: Notify on success + if: needs.build-and-push.result == 'success' && needs.security-scan.result == 'success' + run: | + echo "✅ Docker images built and published successfully!" + echo "Discord Bot: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest" + echo "Web Backend: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest" + + - name: Notify on failure + if: needs.build-and-push.result == 'failure' || needs.security-scan.result == 'failure' + run: | + echo "❌ Docker build or security scan failed!" + exit 1 diff --git a/.github/workflows/docker-compose-test.yml b/.github/workflows/docker-compose-test.yml new file mode 100644 index 0000000..1073d90 --- /dev/null +++ b/.github/workflows/docker-compose-test.yml @@ -0,0 +1,125 @@ +name: Test Docker Compose Setup + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +env: + COMPOSE_PROJECT_NAME: uptime-kuma-test + +jobs: + test-compose: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create test environment file + run: | + cat > .env << EOF + DISCORD_TOKEN=test_token_12345 + GUILD_ID=123456789012345678 + CHANNEL_ID=123456789012345678 + CLIENT_ID=123456789012345678 + UPDATE_TIME=60 + UPTIME_KUMA_URL=https://test.example.com/metrics + UPTIME_KUMA_API_KEY=test_api_key_12345 + EOF + + - name: Build Docker images + run: | + docker-compose build --no-cache + + - name: Start services + run: | + docker-compose up -d + sleep 30 + + - name: Check service health + run: | + # Check if web backend is responding + curl -f http://localhost:8080/back-end.php || exit 1 + + # Check container status + docker-compose ps + + # Check logs for errors + docker-compose logs --tail=50 + + - name: Test web backend endpoint + run: | + # Test the PHP endpoint (should fail gracefully with test data) + response=$(curl -s -w "%{http_code}" http://localhost:8080/back-end.php) + echo "Response: $response" + # Accept both 200 (success) and 500 (expected failure with test data) + if [[ "$response" =~ ^(200|500)$ ]]; then + echo "✅ Web backend endpoint is responding" + else + echo "❌ Web backend endpoint failed" + exit 1 + fi + + - name: Verify container logs + run: | + # Check Discord bot logs for startup + docker-compose logs discord-bot | grep -E "(Bot is online|Error)" || echo "No startup messages found" + + # Check web backend logs + docker-compose logs web-backend | grep -E "(Apache|PHP)" || echo "No backend messages found" + + - name: Cleanup + if: always() + run: | + docker-compose down -v + docker system prune -f + + lint-dockerfiles: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run hadolint on Dockerfiles + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Bot/Dockerfile + failure-threshold: warning + + - name: Run hadolint on Web Dockerfile + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Web/Dockerfile + failure-threshold: warning + + validate-compose: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate docker-compose.yml + run: | + docker-compose config --quiet + echo "✅ docker-compose.yml is valid" + + - name: Check for required environment variables + run: | + # Check if all required env vars are referenced in docker-compose.yml + required_vars=("DISCORD_TOKEN" "GUILD_ID" "CHANNEL_ID" "CLIENT_ID" "UPTIME_KUMA_URL" "UPTIME_KUMA_API_KEY") + + for var in "${required_vars[@]}"; do + if grep -q "\${$var}" docker-compose.yml; then + echo "✅ $var is referenced in docker-compose.yml" + else + echo "❌ $var is missing from docker-compose.yml" + exit 1 + fi + done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..acba42c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,94 @@ +name: Create Release + +on: + push: + tags: + - 'v*' + +env: + REGISTRY: docker.io + IMAGE_NAME_DISCORD_BOT: uptime-kuma-discord-bot + IMAGE_NAME_WEB_BACKEND: uptime-kuma-web-backend + +jobs: + create-release: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/} + VERSION=${VERSION#v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=v$VERSION" >> $GITHUB_OUTPUT + + - name: Generate changelog + id: changelog + run: | + # Get commits since last tag + if git describe --tags --abbrev=0 HEAD~1 >/dev/null 2>&1; then + PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1) + echo "Changes since $PREV_TAG:" + git log --pretty=format:"- %s (%h)" $PREV_TAG..HEAD + else + echo "Initial release" + git log --pretty=format:"- %s (%h)" + fi > CHANGELOG.md + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.version.outputs.tag }} + name: Release ${{ steps.version.outputs.tag }} + body: | + ## What's Changed + + ${{ steps.changelog.outputs.content }} + + ## Docker Images + + ### Discord Bot + ```bash + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:${{ steps.version.outputs.version }} + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest + ``` + + ### Web Backend + ```bash + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:${{ steps.version.outputs.version }} + docker pull ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest + ``` + + ## Quick Start + + ```bash + # Clone the repository + git clone + cd UptimeKuma-DiscordBot + + # Copy environment template + cp env.example .env + + # Edit .env with your values + # Then start with Docker Compose + docker-compose up -d + ``` + + See [DOCKER_SETUP.md](DOCKER_SETUP.md) for detailed instructions. + files: | + CHANGELOG.md + draft: false + prerelease: false + + - name: Update Docker Hub description + if: success() + run: | + echo "🎉 Release ${{ steps.version.outputs.tag }} created successfully!" + echo "Docker images will be automatically built and pushed by the docker-build workflow." diff --git a/Bot/Dockerfile b/Bot/Dockerfile new file mode 100644 index 0000000..c98d7ee --- /dev/null +++ b/Bot/Dockerfile @@ -0,0 +1,32 @@ +# Use Node.js 18 Alpine for smaller image size +FROM node:18-alpine + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci --only=production + +# Copy application code +COPY . . + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S discordbot -u 1001 + +# Change ownership of the app directory +RUN chown -R discordbot:nodejs /app +USER discordbot + +# Expose port (if needed for health checks) +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD node -e "console.log('Bot is running')" || exit 1 + +# Start the bot +CMD ["node", "index.js"] diff --git a/Bot/index.js b/Bot/index.js index 64f3c23..8ebacdb 100644 --- a/Bot/index.js +++ b/Bot/index.js @@ -1,6 +1,15 @@ const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js'); const axios = require('axios'); -const config = require('./config.json'); +// Load configuration from environment variables or config.json +const config = { + token: process.env.DISCORD_TOKEN || require('./config.json').token, + guildID: process.env.GUILD_ID || require('./config.json').guildID, + channelID: process.env.CHANNEL_ID || require('./config.json').channelID, + clientID: process.env.CLIENT_ID || require('./config.json').clientID, + updatetime: parseInt(process.env.UPDATE_TIME) || require('./config.json').updatetime, + backendUrl: process.env.BACKEND_URL || '', + uptimeKumaUrl: process.env.UPTIME_KUMA_URL || '' +}; const client = new Client({ intents: [ @@ -45,7 +54,7 @@ async function updateMessages() { return; } - const response = await axios.get(''); + const response = await axios.get(config.backendUrl); const monitors = response.data; const gamingMonitors = monitors.filter(monitor => [ @@ -96,7 +105,7 @@ async function sendMonitorsMessage(channel, category, monitors) { .setColor('#0099ff') .setDescription(description) .setFooter({ text: `Last updated: ${new Date().toLocaleString()}` }) - .setURL(''); + .setURL(config.uptimeKumaUrl); try { diff --git a/DOCKER_SETUP.md b/DOCKER_SETUP.md new file mode 100644 index 0000000..2340e0e --- /dev/null +++ b/DOCKER_SETUP.md @@ -0,0 +1,236 @@ +# Docker Setup for UptimeKuma Discord Bot + +This guide will help you containerize and run the UptimeKuma Discord Bot using Docker and Docker Compose. + +## Prerequisites + +- Docker and Docker Compose installed on your system +- Uptime Kuma instance running and accessible +- Discord bot application created + +## Quick Start + +1. **Clone the repository** + ```bash + git clone + cd UptimeKuma-DiscordBot + ``` + +2. **Configure environment variables** + ```bash + cp env.example .env + ``` + + Edit the `.env` file with your actual values: + ```env + # Discord Bot Configuration + DISCORD_TOKEN=your_discord_bot_token_here + GUILD_ID=your_discord_guild_id_here + CHANNEL_ID=your_discord_channel_id_here + CLIENT_ID=your_discord_client_id_here + UPDATE_TIME=30 + + # Uptime Kuma Configuration + UPTIME_KUMA_URL=https://your-uptime-kuma-instance.com/metrics + UPTIME_KUMA_API_KEY=your_uptime_kuma_api_key_here + ``` + +3. **Build and start the containers** + ```bash + docker-compose up -d + ``` + +4. **Check the logs** + ```bash + docker-compose logs -f + ``` + +## Configuration Details + +### Environment Variables + +| Variable | Description | Required | +|----------|-------------|----------| +| `DISCORD_TOKEN` | Your Discord bot token | Yes | +| `GUILD_ID` | Discord server (guild) ID | Yes | +| `CHANNEL_ID` | Discord channel ID for status messages | Yes | +| `CLIENT_ID` | Discord bot client ID | Yes | +| `UPDATE_TIME` | Update frequency in seconds (default: 30) | No | +| `UPTIME_KUMA_URL` | Full URL to your Uptime Kuma metrics endpoint | Yes | +| `UPTIME_KUMA_API_KEY` | API key from Uptime Kuma dashboard | Yes | + +### Services + +#### Discord Bot (`discord-bot`) +- **Image**: Built from `Bot/Dockerfile` +- **Port**: Internal only (no external port) +- **Health Check**: Node.js process check +- **Dependencies**: `web-backend` + +#### Web Backend (`web-backend`) +- **Image**: Built from `Web/Dockerfile` +- **Port**: 8080 (external) → 80 (internal) +- **Health Check**: HTTP endpoint check +- **Access**: `http://localhost:8080/back-end.php` + +## Docker Commands + +### Build and Start +```bash +# Build and start all services +docker-compose up -d + +# Build and start with rebuild +docker-compose up -d --build + +# Start specific service +docker-compose up -d discord-bot +``` + +### Management +```bash +# View logs +docker-compose logs -f +docker-compose logs -f discord-bot +docker-compose logs -f web-backend + +# Stop services +docker-compose down + +# Stop and remove volumes +docker-compose down -v + +# Restart services +docker-compose restart + +# Update services +docker-compose pull +docker-compose up -d +``` + +### Debugging +```bash +# Execute shell in running container +docker-compose exec discord-bot sh +docker-compose exec web-backend bash + +# View container status +docker-compose ps + +# View resource usage +docker stats +``` + +## Troubleshooting + +### Common Issues + +1. **Bot not connecting to Discord** + - Verify `DISCORD_TOKEN` is correct + - Check Discord bot permissions + - Ensure bot is invited to the server + +2. **Backend not responding** + - Verify `UPTIME_KUMA_URL` and `UPTIME_KUMA_API_KEY` + - Check Uptime Kuma instance accessibility + - Test API endpoint manually: `curl http://localhost:8080/back-end.php` + +3. **Permission errors** + - Ensure Docker has proper permissions + - Check file ownership in mounted volumes + +4. **Container health checks failing** + - Check logs: `docker-compose logs` + - Verify all environment variables are set + - Test individual services + +### Health Checks + +Both services include health checks: +- **Discord Bot**: Checks if Node.js process is running +- **Web Backend**: Checks if PHP endpoint responds + +View health status: +```bash +docker-compose ps +``` + +### Logs + +Monitor logs in real-time: +```bash +# All services +docker-compose logs -f + +# Specific service +docker-compose logs -f discord-bot +docker-compose logs -f web-backend + +# Last 100 lines +docker-compose logs --tail=100 +``` + +## Security Considerations + +- Both containers run as non-root users +- Environment variables are used instead of hardcoded secrets +- Network isolation between services +- Health checks for monitoring + +## Production Deployment + +For production deployment: + +1. **Use Docker secrets** for sensitive data +2. **Set up reverse proxy** (nginx/traefik) for SSL termination +3. **Configure log rotation** and monitoring +4. **Use Docker Swarm or Kubernetes** for orchestration +5. **Set up backup strategies** for persistent data + +### Example Production docker-compose.yml +```yaml +version: '3.8' +services: + discord-bot: + # ... existing config ... + deploy: + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + secrets: + - discord_token + - uptime_kuma_api_key + + web-backend: + # ... existing config ... + deploy: + replicas: 2 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + +secrets: + discord_token: + external: true + uptime_kuma_api_key: + external: true +``` + +## Support + +For issues and questions: +- Check the logs first: `docker-compose logs -f` +- Verify configuration: `docker-compose config` +- Test individual services +- Review this documentation + +## Contributing + +When contributing Docker-related changes: +1. Test with `docker-compose up -d --build` +2. Verify health checks pass +3. Test with different environment configurations +4. Update this documentation if needed diff --git a/GITEA_ACTIONS_SETUP.md b/GITEA_ACTIONS_SETUP.md new file mode 100644 index 0000000..84634fa --- /dev/null +++ b/GITEA_ACTIONS_SETUP.md @@ -0,0 +1,303 @@ +# Gitea Actions Setup for Docker Build and Publish + +This document explains how to set up Gitea Actions to automatically build and publish Docker images for the UptimeKuma Discord Bot project. + +## Overview + +The Gitea Actions workflows provide: +- **Automated Docker image building** for both Discord bot and web backend +- **Multi-platform support** (linux/amd64, linux/arm64) +- **Security scanning** with Trivy +- **Automated releases** with changelog generation +- **Docker Compose testing** to ensure everything works together + +## Workflows + +### 1. Docker Build (`docker-build.yml`) + +**Triggers:** +- Push to `main` or `develop` branches +- Push of version tags (`v*`) +- Pull requests to `main` + +**Features:** +- Builds both Discord bot and web backend images +- Multi-platform builds (AMD64, ARM64) +- Pushes to Docker Hub on non-PR events +- Generates Software Bill of Materials (SBOM) +- Security vulnerability scanning +- GitHub Actions cache for faster builds + +### 2. Docker Compose Test (`docker-compose-test.yml`) + +**Triggers:** +- Push to `main` or `develop` branches +- Pull requests to `main` + +**Features:** +- Tests the complete Docker Compose setup +- Validates Dockerfile syntax with hadolint +- Checks service health and connectivity +- Validates environment variable configuration + +### 3. Release (`release.yml`) + +**Triggers:** +- Push of version tags (`v*`) + +**Features:** +- Creates GitHub releases automatically +- Generates changelog from git commits +- Updates release notes with Docker image information + +## Required Secrets + +Configure these secrets in your Gitea repository settings: + +### Docker Hub Secrets +- `DOCKER_USERNAME`: Your Docker Hub username +- `DOCKER_PASSWORD`: Your Docker Hub access token (not password) + +### How to Get Docker Hub Token +1. Go to [Docker Hub](https://hub.docker.com/) +2. Sign in to your account +3. Go to Account Settings → Security +4. Create a new access token +5. Copy the token and use it as `DOCKER_PASSWORD` + +## Setup Instructions + +### 1. Enable Gitea Actions + +1. Go to your Gitea repository +2. Navigate to **Settings** → **Actions** +3. Enable **Actions** if not already enabled + +### 2. Configure Secrets + +1. Go to **Settings** → **Secrets** +2. Add the following secrets: + +| Secret Name | Description | Example | +|-------------|-------------|---------| +| `DOCKER_USERNAME` | Docker Hub username | `myusername` | +| `DOCKER_PASSWORD` | Docker Hub access token | `dckr_pat_...` | + +### 3. Test the Workflow + +1. Push a commit to the `main` branch +2. Go to **Actions** tab in your repository +3. Watch the workflow execution +4. Check the logs for any errors + +## Workflow Details + +### Docker Build Workflow + +```yaml +# Key features: +- Multi-platform builds (linux/amd64, linux/arm64) +- Automatic tagging based on git refs +- Build cache for faster subsequent builds +- Security scanning with Trivy +- SBOM generation for supply chain security +``` + +**Image Tags Generated:** +- `latest` (for main branch) +- `main` (branch name) +- `v1.0.0` (version tags) +- `1.0` (major.minor) +- `1` (major) + +### Docker Compose Test Workflow + +```yaml +# Key features: +- Validates Docker Compose configuration +- Tests service startup and health checks +- Lints Dockerfiles with hadolint +- Validates environment variable usage +``` + +### Release Workflow + +```yaml +# Key features: +- Automatic release creation on version tags +- Changelog generation from git commits +- Release notes with Docker image information +- Quick start instructions included +``` + +## Usage Examples + +### Creating a Release + +1. **Create and push a version tag:** + ```bash + git tag v1.0.0 + git push origin v1.0.0 + ``` + +2. **The workflow will:** + - Build Docker images with version tags + - Create a GitHub release + - Generate changelog + - Publish images to Docker Hub + +### Testing Changes + +1. **Create a pull request:** + ```bash + git checkout -b feature/new-feature + git commit -m "Add new feature" + git push origin feature/new-feature + ``` + +2. **The workflow will:** + - Build images (but not push) + - Run security scans + - Test Docker Compose setup + - Validate configuration + +## Monitoring and Troubleshooting + +### Viewing Workflow Runs + +1. Go to **Actions** tab in your repository +2. Click on a workflow run to see details +3. Expand job steps to view logs + +### Common Issues + +#### 1. Docker Hub Authentication Failed +``` +Error: Cannot perform an interactive login from a non TTY device +``` +**Solution:** Check that `DOCKER_USERNAME` and `DOCKER_PASSWORD` secrets are correctly set. + +#### 2. Build Cache Issues +``` +Error: failed to solve: failed to compute cache key +``` +**Solution:** The workflow will automatically retry without cache on failure. + +#### 3. Security Scan Failures +``` +Error: Trivy found vulnerabilities +``` +**Solution:** Review the security scan results and update base images if needed. + +### Workflow Status Badge + +Add this to your README.md to show workflow status: + +```markdown +[![Docker Build](https://gitea.example.com/api/badges/username/repo/status.svg)](https://gitea.example.com/username/repo/actions) +``` + +## Advanced Configuration + +### Custom Registry + +To use a different registry than Docker Hub: + +1. **Update environment variables in workflows:** + ```yaml + env: + REGISTRY: your-registry.com + ``` + +2. **Update image names:** + ```yaml + env: + IMAGE_NAME_DISCORD_BOT: your-org/uptime-kuma-discord-bot + IMAGE_NAME_WEB_BACKEND: your-org/uptime-kuma-web-backend + ``` + +### Build Arguments + +To pass build arguments to Docker builds: + +```yaml +- name: Build and push Discord Bot image + uses: docker/build-push-action@v5 + with: + context: ./Bot + file: ./Bot/Dockerfile + build-args: | + NODE_VERSION=18 + BUILD_DATE=${{ github.event.head_commit.timestamp }} +``` + +### Matrix Builds + +To build for multiple Node.js versions: + +```yaml +strategy: + matrix: + node-version: [16, 18, 20] +``` + +## Security Considerations + +### Secrets Management +- Never commit secrets to the repository +- Use Gitea's built-in secrets management +- Rotate Docker Hub tokens regularly +- Use least-privilege access tokens + +### Image Security +- Base images are automatically scanned for vulnerabilities +- SBOMs are generated for supply chain transparency +- Multi-platform builds ensure compatibility +- Non-root users in containers for security + +### Workflow Security +- Workflows run in isolated environments +- Secrets are encrypted and only available during execution +- No secrets are logged or exposed in outputs + +## Best Practices + +### 1. Version Tagging +- Use semantic versioning (`v1.0.0`) +- Tag releases consistently +- Include meaningful commit messages + +### 2. Branch Strategy +- Use `main` for production releases +- Use `develop` for integration testing +- Use feature branches for new development + +### 3. Monitoring +- Monitor workflow execution regularly +- Set up notifications for failures +- Review security scan results + +### 4. Maintenance +- Keep base images updated +- Review and update workflow dependencies +- Monitor for deprecated actions + +## Support + +For issues with Gitea Actions: + +1. Check the workflow logs first +2. Verify secrets are correctly configured +3. Ensure Gitea Actions is enabled +4. Check Gitea version compatibility +5. Review this documentation + +## Contributing + +When contributing to the workflows: + +1. Test changes in a fork first +2. Update documentation as needed +3. Follow the existing workflow patterns +4. Ensure backward compatibility +5. Add appropriate error handling diff --git a/Web/Dockerfile b/Web/Dockerfile new file mode 100644 index 0000000..6458fa2 --- /dev/null +++ b/Web/Dockerfile @@ -0,0 +1,39 @@ +# Use PHP 8.2 Apache for web backend +FROM php:8.2-apache + +# Install required PHP extensions +RUN apt-get update && apt-get install -y \ + libcurl4-openssl-dev \ + pkg-config \ + libssl-dev \ + && docker-php-ext-install curl \ + && rm -rf /var/lib/apt/lists/* + +# Enable Apache mod_rewrite +RUN a2enmod rewrite + +# Set working directory +WORKDIR /var/www/html + +# Copy PHP application +COPY back-end.php . + +# Create non-root user for security +RUN groupadd -g 1001 phpapp && \ + useradd -r -u 1001 -g phpapp phpapp + +# Change ownership +RUN chown -R phpapp:phpapp /var/www/html + +# Switch to non-root user +USER phpapp + +# Expose port 80 +EXPOSE 80 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost/back-end.php || exit 1 + +# Start Apache +CMD ["apache2-foreground"] diff --git a/Web/back-end.php b/Web/back-end.php index df60abc..368012a 100644 --- a/Web/back-end.php +++ b/Web/back-end.php @@ -1,8 +1,8 @@ /metrics'; //your URL +$url = $_ENV['UPTIME_KUMA_URL'] ?? '/metrics'; //your URL // Retrieve an API key from the UptimeKuma dashboard here /settings/api-keys -$password = '***************'; //your API key +$password = $_ENV['UPTIME_KUMA_API_KEY'] ?? '***************'; //your API key $username = ''; //leave empty // Initialize a new cURL session diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..68ad5c3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +version: '3.8' + +services: + # Discord Bot Service + discord-bot: + build: + context: ./Bot + dockerfile: Dockerfile + container_name: uptime-kuma-discord-bot + restart: unless-stopped + environment: + - DISCORD_TOKEN=${DISCORD_TOKEN} + - GUILD_ID=${GUILD_ID} + - CHANNEL_ID=${CHANNEL_ID} + - CLIENT_ID=${CLIENT_ID} + - UPDATE_TIME=${UPDATE_TIME:-30} + - BACKEND_URL=http://web-backend:80/back-end.php + - UPTIME_KUMA_URL=${UPTIME_KUMA_URL} + depends_on: + - web-backend + networks: + - uptime-kuma-network + volumes: + - ./Bot/config.json:/app/config.json:ro + healthcheck: + test: ["CMD", "node", "-e", "console.log('Bot is running')"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # PHP Web Backend Service + web-backend: + build: + context: ./Web + dockerfile: Dockerfile + container_name: uptime-kuma-web-backend + restart: unless-stopped + environment: + - UPTIME_KUMA_URL=${UPTIME_KUMA_URL} + - UPTIME_KUMA_API_KEY=${UPTIME_KUMA_API_KEY} + ports: + - "8080:80" + networks: + - uptime-kuma-network + volumes: + - ./Web/back-end.php:/var/www/html/back-end.php:ro + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/back-end.php"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + +networks: + uptime-kuma-network: + driver: bridge + +volumes: + bot-config: + web-config: diff --git a/env.example b/env.example new file mode 100644 index 0000000..973cbca --- /dev/null +++ b/env.example @@ -0,0 +1,13 @@ +# Discord Bot Configuration +DISCORD_TOKEN=your_discord_bot_token_here +GUILD_ID=your_discord_guild_id_here +CHANNEL_ID=your_discord_channel_id_here +CLIENT_ID=your_discord_client_id_here +UPDATE_TIME=30 + +# Uptime Kuma Configuration +UPTIME_KUMA_URL=https://your-uptime-kuma-instance.com/metrics +UPTIME_KUMA_API_KEY=your_uptime_kuma_api_key_here + +# Optional: Override backend URL (defaults to internal Docker network) +# BACKEND_URL=http://web-backend:80/back-end.php