From 6411883f58ba150cd0513df9424f4057e652a372 Mon Sep 17 00:00:00 2001 From: Evercatch Date: Wed, 18 Feb 2026 15:30:47 +0000 Subject: [PATCH] Initial commit --- .env.example | 39 +++++++ .gitea/issue_template/bug_report.yaml | 44 +++++++ .gitea/issue_template/documentation.yaml | 35 ++++++ .../security_vulnerability.yaml | 46 ++++++++ .gitea/pull_request_template.md | 20 ++++ .gitea/workflows/pr-management.yml | 88 ++++++++++++++ .gitea/workflows/pr-title-checker.yml | 110 ++++++++++++++++++ .gitignore | 47 ++++++++ CODEOWNERS | 5 + CONTRIBUTING.md | 63 ++++++++++ LICENSE | 8 ++ README.md | 72 ++++++++++++ SECURITY.md | 24 ++++ 13 files changed, 601 insertions(+) create mode 100644 .env.example create mode 100644 .gitea/issue_template/bug_report.yaml create mode 100644 .gitea/issue_template/documentation.yaml create mode 100644 .gitea/issue_template/security_vulnerability.yaml create mode 100644 .gitea/pull_request_template.md create mode 100644 .gitea/workflows/pr-management.yml create mode 100644 .gitea/workflows/pr-title-checker.yml create mode 100644 .gitignore create mode 100644 CODEOWNERS create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SECURITY.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bb0023b --- /dev/null +++ b/.env.example @@ -0,0 +1,39 @@ +# ───────────────────────────────────────────── +# Evercatch — Environment Variables +# Copy this file to .env and fill in values. +# NEVER commit .env to version control. +# ───────────────────────────────────────────── + +# App +APP_ENV=development +APP_PORT=8000 +APP_SECRET_KEY=change-me + +# Database — PostgreSQL +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_DB=evercatch +POSTGRES_USER=evercatch +POSTGRES_PASSWORD=change-me + +# Database — MongoDB +MONGO_URI=mongodb://localhost:27017 +MONGO_DB=evercatch_events + +# Redis +REDIS_URL=redis://localhost:6379 + +# Stripe +STRIPE_SECRET_KEY=sk_test_... +STRIPE_WEBHOOK_SECRET=whsec_... +STRIPE_PRICE_ID_INDIE=price_... +STRIPE_PRICE_ID_PRO=price_... +STRIPE_PRICE_ID_BUSINESS=price_... + +# Providers +SENDGRID_WEBHOOK_KEY= +SHOPIFY_WEBHOOK_SECRET= +GITHUB_WEBHOOK_SECRET= + +# Internal +BOT_ACCESS_TOKEN= diff --git a/.gitea/issue_template/bug_report.yaml b/.gitea/issue_template/bug_report.yaml new file mode 100644 index 0000000..9937b8f --- /dev/null +++ b/.gitea/issue_template/bug_report.yaml @@ -0,0 +1,44 @@ +name: Bug Report +about: Report a bug specific to this repository (API, tests, CI, etc.) +labels: + - "type: bug" +body: + - type: markdown + attributes: + value: | + **Note:** For product-level bugs and feature requests, use [Evercatch Board](https://git.psmattas.com/Evercatch/evercatch-board/issues). + This template is for repo-specific issues (broken tests, CI failures, dependency problems, etc.). + - type: input + id: summary + attributes: + label: Summary + placeholder: Brief description of the bug + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + value: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual Behavior + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional Context + description: Logs, screenshots, environment details, etc. diff --git a/.gitea/issue_template/documentation.yaml b/.gitea/issue_template/documentation.yaml new file mode 100644 index 0000000..d97e688 --- /dev/null +++ b/.gitea/issue_template/documentation.yaml @@ -0,0 +1,35 @@ +name: Documentation +about: Report missing, incorrect, or outdated documentation in this repo +labels: + - "type: docs" +body: + - type: markdown + attributes: + value: | + Use this for documentation issues within this repository (README, inline docs, API docs, setup guides). + For product-level documentation requests, use [Evercatch Board](https://git.psmattas.com/Evercatch/evercatch-board/issues). + - type: input + id: page + attributes: + label: Affected Page / File + placeholder: e.g. README.md, monitoring/README.md, app/api/v1/webhooks.py + validations: + required: true + - type: dropdown + id: type + attributes: + label: Type + options: + - Missing documentation + - Incorrect / outdated information + - Unclear or confusing + - Typo / formatting + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: What needs to change and why? + validations: + required: true diff --git a/.gitea/issue_template/security_vulnerability.yaml b/.gitea/issue_template/security_vulnerability.yaml new file mode 100644 index 0000000..4204448 --- /dev/null +++ b/.gitea/issue_template/security_vulnerability.yaml @@ -0,0 +1,46 @@ +name: Security Vulnerability +about: Report a security vulnerability in this repository +labels: + - "type: security" + - "priority: critical" +body: + - type: markdown + attributes: + value: | + **Please do not disclose sensitive details publicly.** If this is a critical vulnerability, + contact the maintainers directly before filing a public issue. + - type: input + id: summary + attributes: + label: Summary + placeholder: Brief description of the vulnerability + validations: + required: true + - type: dropdown + id: severity + attributes: + label: Severity + options: + - Critical — active exploit / data exposure + - High — exploitable with moderate effort + - Medium — limited impact or requires specific conditions + - Low — informational / hardening suggestion + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: What is the vulnerability and how can it be exploited? + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to Reproduce + description: Provide enough detail for someone to verify the issue. + - type: textarea + id: remediation + attributes: + label: Suggested Remediation + description: If you have a fix in mind, describe it here. diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md new file mode 100644 index 0000000..40c30aa --- /dev/null +++ b/.gitea/pull_request_template.md @@ -0,0 +1,20 @@ +### Ticket + + + +Resolves Evercatch/evercatch-board# + +### Summary + + + +### Changes + +- +- + +### Checklist + +- [ ] Self-reviewed +- [ ] Tests added/updated and passing +- [ ] Docs updated (if applicable) diff --git a/.gitea/workflows/pr-management.yml b/.gitea/workflows/pr-management.yml new file mode 100644 index 0000000..525b78e --- /dev/null +++ b/.gitea/workflows/pr-management.yml @@ -0,0 +1,88 @@ +name: PR Management Bot + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + branches: [main] + +jobs: + pr-bot: + runs-on: ubuntu-latest + env: + API_TOKEN: ${{ secrets.BOT_ACCESS_TOKEN }} + API_BASE_URL: https://git.psmattas.com/api/v1 + REPO: ${{ gitea.repository }} + PR_NUMBER: ${{ gitea.event.pull_request.number }} + PR_AUTHOR: ${{ gitea.actor }} + + steps: + # FIX 1: Use standard short syntax to avoid upstream gitea.com connection errors + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Auto-label from commit messages + run: | + API_URL="$API_BASE_URL/repos/$REPO/pulls/$PR_NUMBER/commits" + MESSAGES=$(curl -s -H "Authorization: token $API_TOKEN" "$API_URL" | jq -r '.[].commit.message') + + # Match "EC-NUM: TYPE" or standalone "TYPE:" at start of commit message + UNIQUE_TYPES=$(echo "$MESSAGES" | grep -oP '^(EC-\d+: )?\K(FEAT|FIX|DOCS|STYLE|REFACTOR|PERF|TEST|CHORE)(?=:)' | sort -u || true) + + if [[ -z "$UNIQUE_TYPES" ]]; then + echo "No semantic commit types found." + exit 0 + fi + + LABELS_JSON=$(echo "$UNIQUE_TYPES" | jq -R '("type: " + (. | ascii_downcase))' | jq -s '{"labels": .}') + ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER/labels" + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS_JSON" "$ISSUES_API_URL" + + - name: Auto-assign author + if: gitea.event.pull_request.assignees_count == 0 && gitea.actor != 'gitea-actions' && gitea.actor != 'EC-bot' + run: | + ISSUES_EDIT_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER" + JSON_PAYLOAD=$(echo "$PR_AUTHOR" | jq -R '{"assignees": [.]}') + curl -s -f -X PATCH -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$JSON_PAYLOAD" "$ISSUES_EDIT_API_URL" + + - name: Add conflict label and comment + if: gitea.event.pull_request.merge_status == 'CONFLICT' + run: | + COMMENT_BODY="🚨 **Merge Conflict Detected** 🚨\n\nHi @$PR_AUTHOR, this pull request has merge conflicts with the \`main\` branch. Please resolve them by rebasing or merging \`main\` into your branch so the PR can be reviewed." + COMMENT_JSON=$(jq -n --arg body "$COMMENT_BODY" '{"body": $body}') + ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER/comments" + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$ISSUES_API_URL" + + LABEL_JSON='{ "labels": ["status: needs-rebase"] }' + LABELS_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER/labels" + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$LABELS_API_URL" + + - name: Remove 'needs-rebase' label if clean + if: gitea.event.pull_request.merge_status != 'CONFLICT' + run: | + LABEL_TO_REMOVE="status: needs-rebase" + ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER" + + CURRENT_LABELS_JSON=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels") + LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$LABEL_TO_REMOVE" '.[] | select(.name == $name) | .id') + + if [ -n "$LABEL_ID" ]; then + echo "Found and removing '$LABEL_TO_REMOVE' (ID: $LABEL_ID)..." + curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$LABEL_ID" + fi + + - name: Auto-assign reviewers + run: | + target_reviewer="psmattas" + + # FIX 3: Check if the author is the target reviewer to avoid API error (422) + if [[ "$PR_AUTHOR" == "$target_reviewer" ]]; then + echo "Author is $target_reviewer. Skipping self-review request." + exit 0 + fi + + REVIEWERS_JSON="{\"reviewers\": [\"$target_reviewer\"]}" + echo "Requesting review from: $target_reviewer" + API_URL="$API_BASE_URL/repos/$REPO/pulls/$PR_NUMBER/requested_reviewers" + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$REVIEWERS_JSON" "$API_URL" diff --git a/.gitea/workflows/pr-title-checker.yml b/.gitea/workflows/pr-title-checker.yml new file mode 100644 index 0000000..2dc6154 --- /dev/null +++ b/.gitea/workflows/pr-title-checker.yml @@ -0,0 +1,110 @@ +name: PR Title Checker + +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + enforce-pr-title: + name: Validate PR Title Format + runs-on: ubuntu-latest + steps: + - name: Check PR Title and Manage Labels/Comments + env: + API_TOKEN: ${{ secrets.BOT_ACCESS_TOKEN }} + API_BASE_URL: https://git.psmattas.com/api/v1 + REPO: ${{ gitea.repository }} + PR_NUMBER: ${{ gitea.event.pull_request.number }} + PR_TITLE: ${{ gitea.event.pull_request.title }} + COMMENT_BODY_INVALID: | + Hi @${{ gitea.actor }}, thanks for the contribution! + + The title of this pull request does not follow the required format. This PR is currently **blocked from merging**. + + Please update the title to match one of these formats: + + **Board issue:** `EC-[NUMBER]: [TYPE]: Description` + **Repo-only:** `[TYPE]: Description` + + - **Valid types:** `FEAT`, `FIX`, `DOCS`, `REFACTOR`, `STYLE`, `PERF`, `TEST`, `CHORE` + - **Examples:** + - `EC-10: FEAT: Add monitoring stack` + - `FIX: Correct typo in health endpoint` + + PRs with an `EC-` prefix automatically reference `Evercatch/evercatch-board#`. + + + COMMENT_BODY_WIP: | + Hi @${{ gitea.actor }}, this pull request has been marked as a **Work in Progress**. + + While in this state, it is **blocked from merging**. + + When the PR is ready for review, please remove the `WIP:` prefix from the title. This check will re-run automatically and remove the blocking label. + + + run: | + WIP_LABEL="status: work-in-progress" + INVALID_LABEL="status: invalid-title" + WIP_REGEX="^WIP:" + # Accept either "EC-NUM: TYPE: desc" or "TYPE: desc" + FORMAT_REGEX="^(EC-[0-9]+: )?(FEAT|FIX|DOCS|REFACTOR|STYLE|PERF|TEST|CHORE): .+$" + ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER" + + if [ -z "$PR_NUMBER" ]; then + echo "::error::Could not determine PR Number. Aborting." + exit 1 + fi + + if [[ "$PR_TITLE" =~ $WIP_REGEX ]]; then + echo "ℹ️ PR is marked as a Work in Progress." + LABEL_JSON=$(echo "$WIP_LABEL" | jq -R '{"labels": [.]}') + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$ISSUES_API_URL/labels" + + WIP_COMMENT_IDENTIFIER="" + EXISTING_COMMENTS=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/comments") + if ! echo "$EXISTING_COMMENTS" | grep -q "$WIP_COMMENT_IDENTIFIER"; then + echo "Posting WIP comment." + COMMENT_JSON=$(jq -n --arg body "$COMMENT_BODY_WIP" '{"body": $body}') + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$ISSUES_API_URL/comments" + fi + exit 1 + + elif [[ "$PR_TITLE" =~ $FORMAT_REGEX ]]; then + echo "✅ PR title format is correct and not a WIP." + echo "Checking for labels to remove..." + + CURRENT_LABELS_JSON=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels") + + INVALID_LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$INVALID_LABEL" '.[] | select(.name == $name) | .id') + if [ -n "$INVALID_LABEL_ID" ]; then + echo "Found and removing '$INVALID_LABEL' (ID: $INVALID_LABEL_ID)..." + curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$INVALID_LABEL_ID" + fi + + WIP_LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$WIP_LABEL" '.[] | select(.name == $name) | .id') + if [ -n "$WIP_LABEL_ID" ]; then + echo "Found and removing '$WIP_LABEL' (ID: $WIP_LABEL_ID)..." + curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$WIP_LABEL_ID" + fi + + exit 0 + + else + echo "❌ ERROR: PR title does not match the required format." + echo "Adding '$INVALID_LABEL' label..." + LABEL_JSON=$(echo "$INVALID_LABEL" | jq -R '{"labels": [.]}') + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$ISSUES_API_URL/labels" + + # Remove WIP label just in case + ENCODED_WIP_LABEL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$WIP_LABEL'))") + curl -s -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$ENCODED_WIP_LABEL" || true + + INVALID_COMMENT_IDENTIFIER="" + EXISTING_COMMENTS=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/comments") + if ! echo "$EXISTING_COMMENTS" | grep -q "$INVALID_COMMENT_IDENTIFIER"; then + echo "Posting invalid title comment." + COMMENT_JSON=$(jq -n --arg body "$COMMENT_BODY_INVALID" '{"body": $body}') + curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$ISSUES_API_URL/comments" + fi + exit 1 + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0be5d15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Environment +.env +.env.* +!.env.example + +# Python +__pycache__/ +*.py[cod] +*.pyo +*.pyd +.Python +*.egg-info/ +dist/ +build/ +.venv/ +venv/ +*.log + +# Node / Angular +node_modules/ +dist/ +.angular/ +*.tsbuildinfo + +# Docker +.docker/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store +Thumbs.db + +# Coverage +.coverage +htmlcov/ +coverage/ +*.lcov + +# Secrets +*.pem +*.key +*.p12 +*.pfx +secrets/ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..225c647 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,5 @@ +# Evercatch Code Owners +# These users are automatically requested for review on PRs. +# Format: path @username + +* @psmattas diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..475de33 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# Contributing to Evercatch + +Thank you for contributing. Please read this guide before opening issues or PRs. + +--- + +## 🌿 Branching + +Branch from `main` using the format `EC-ID-short-description`. + +```bash +git checkout -b EC-12-implement-stripe-inbound +``` + +--- + +## ✨ Commit Messages + +Follow the Conventional Commits standard prefixed with the issue ID. + +**Format:** `EC-00: (scope): message` + +| Type | Description | +| :--- | :--- | +| `feat` | New feature | +| `fix` | Bug fix | +| `docs` | Documentation | +| `style` | Formatting | +| `refactor` | Refactor | +| `perf` | Performance | +| `test` | Tests | +| `build` | Build system | +| `ci` | CI/CD config | +| `chore` | Maintenance | + +**Examples:** +- `EC-12: feat(webhooks): implement stripe inbound endpoint` +- `EC-18: fix(relay): resolve double-delivery on retry` + +--- + +## 🔍 Pull Requests + +- Link the issue in your PR description +- All CI checks must pass before requesting review +- Minimum one approval required to merge +- Keep PRs focused — one feature or fix per PR + +--- + +## 🏷️ Labels + +Apply the appropriate labels to your issue or PR: + +- `type:` — what kind of change is this +- `priority:` — how urgent is it +- `status:` — current state of the issue or PR + +--- + +## ❓ Questions + +Open an issue with the `type: chore` label or reach out via the org README. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..689c1be --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2026 Evercatch. All rights reserved. + +This software and its source code are proprietary and confidential. +Unauthorised copying, distribution, modification, or use of this software, +in whole or in part, via any medium, is strictly prohibited without the +prior written permission of Evercatch. + +For licensing enquiries, contact: legal@evercatch.io diff --git a/README.md b/README.md new file mode 100644 index 0000000..788863e --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# 📦 Repository Name + +> Short one-line description of what this repository does. + +--- + +## 🧭 Overview + +Describe what this service/module is responsible for within the Evercatch platform. + +--- + +## 🛠️ Tech Stack + +| Layer | Technology | +| :--- | :--- | +| Language | — | +| Framework | — | +| Key Dependencies | — | + +--- + +## 🚀 Getting Started + +### Prerequisites + +- Docker & Docker Compose +- Node.js / Python (specify version) + +### Local Development + +```bash +# Clone the repo +git clone https://git.psmattas.com/Evercatch/REPO_NAME.git +cd REPO_NAME + +# Copy environment variables +cp .env.example .env + +# Start services +docker compose up -d +``` + +--- + +## 🌿 Branching & Commits + +All work follows the Evercatch contribution guide defined in the org README. + +**Branch format:** `EC-ID-short-description` +**Commit format:** `EC-00: type(scope): message` + +See [Evercatch Org README](https://git.psmattas.com/Evercatch) for full conventions. + +--- + +## ⚙️ CI/CD + +Automated via Jenkins. Merges to `main` trigger staging deployments. + +| Pipeline | Status | +| :--- | :---: | +| Build | ![Build Status](https://img.shields.io/badge/build-pending-lightgrey) | +| Lint | ![Lint Status](https://img.shields.io/badge/lint-pending-lightgrey) | +| Test | ![Test Status](https://img.shields.io/badge/tests-pending-lightgrey) | + +--- + +## 📄 License + +**Copyright © 2026 Evercatch.** +Proprietary and confidential. Unauthorised distribution is strictly prohibited. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..088a2c2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,24 @@ +# Security Policy + +## Reporting a Vulnerability + +If you discover a security vulnerability in any Evercatch repository, +please do **not** open a public issue. + +Report it privately to: **security@evercatch.io** + +Include: +- A description of the vulnerability +- Steps to reproduce +- Potential impact +- Any suggested fixes if available + +We will acknowledge receipt within 48 hours and aim to release a fix +within 14 days depending on severity. + +## Supported Versions + +| Version | Supported | +| :--- | :---: | +| Latest `main` | ✅ | +| Older releases | ❌ |