commit 8a924f60918dc2d0fb7afe6509fe89205c8d743b Author: Ehsan.Asadi Date: Fri Dec 26 15:52:50 2025 +0330 [INIT-001] Initial project setup with Clean Architecture (feat) - Implemented Clean Architecture with Domain, Application, Infrastructure, Presentation layers - Added comprehensive project structure following SOLID principles - Created Kubernetes deployment with Helm charts (HPA, PDB, NetworkPolicy) - Configured ArgoCD for automated deployment (production + staging) - Implemented CI/CD pipeline with GitHub Actions - Added comprehensive documentation (handbook, architecture, coding standards) - Configured PostgreSQL, Redis, Celery for backend services - Created modern landing page with Persian fonts (Vazirmatn) - Added Docker multi-stage build for production - Configured development tools (pytest, black, flake8, mypy, isort) - Added pre-commit hooks for code quality - Implemented Makefile for common operations diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..ea84793 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,193 @@ +name: CD - Build & Deploy + +on: + push: + branches: [ main ] + tags: + - 'v*' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + outputs: + image-tag: ${{ steps.meta.outputs.tags }} + image-version: ${{ steps.meta.outputs.version }} + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + 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=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max + platforms: linux/amd64 + + - name: Image digest + run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" + + package-helm: + runs-on: ubuntu-latest + needs: build-and-push + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: 'latest' + + - name: Package Helm chart + run: | + helm package helm/peikarband --destination . + helm repo index . --url https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }} + + - name: Upload Helm chart artifact + uses: actions/upload-artifact@v3 + with: + name: helm-chart + path: | + *.tgz + index.yaml + + deploy-staging: + runs-on: ubuntu-latest + needs: [build-and-push, package-helm] + if: github.ref == 'refs/heads/main' + environment: + name: staging + url: https://staging.peikarband.ir + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install kubectl + uses: azure/setup-kubectl@v3 + + - name: Install Helm + uses: azure/setup-helm@v3 + + - name: Configure kubectl + run: | + echo "${{ secrets.KUBECONFIG_STAGING }}" | base64 -d > kubeconfig + export KUBECONFIG=kubeconfig + + - name: Deploy to Staging + run: | + export KUBECONFIG=kubeconfig + helm upgrade --install peikarband-staging ./helm/peikarband \ + --namespace staging \ + --create-namespace \ + --set image.tag=${{ needs.build-and-push.outputs.image-version }} \ + --set image.repository=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} \ + --set ingress.hosts[0].host=staging.peikarband.ir \ + --wait \ + --timeout 5m + + deploy-production: + runs-on: ubuntu-latest + needs: [build-and-push, package-helm] + if: startsWith(github.ref, 'refs/tags/v') + environment: + name: production + url: https://peikarband.ir + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install kubectl + uses: azure/setup-kubectl@v3 + + - name: Install Helm + uses: azure/setup-helm@v3 + + - name: Configure kubectl + run: | + echo "${{ secrets.KUBECONFIG_PRODUCTION }}" | base64 -d > kubeconfig + export KUBECONFIG=kubeconfig + + - name: Deploy to Production + run: | + export KUBECONFIG=kubeconfig + helm upgrade --install peikarband-prod ./helm/peikarband \ + --namespace production \ + --create-namespace \ + --set image.tag=${{ needs.build-and-push.outputs.image-version }} \ + --set image.repository=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} \ + --set replicaCount=3 \ + --set autoscaling.enabled=true \ + --values helm/peikarband/values-production.yaml \ + --wait \ + --timeout 10m + + - name: Verify deployment + run: | + export KUBECONFIG=kubeconfig + kubectl rollout status deployment/peikarband-prod -n production + kubectl get pods -n production + + release: + runs-on: ubuntu-latest + needs: [build-and-push, package-helm, deploy-production] + if: startsWith(github.ref, 'refs/tags/v') + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Download Helm chart + uses: actions/download-artifact@v3 + with: + name: helm-chart + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + files: | + *.tgz + index.yaml + generate_release_notes: true + draft: false + prerelease: false + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7313779 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,121 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ['3.11', '3.12'] + + services: + postgres: + image: postgres:14 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: peikarband_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache pip packages + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-dev.txt + + - name: Lint with flake8 + run: | + flake8 src/ --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 src/ --count --max-complexity=10 --max-line-length=120 --statistics + + - name: Type check with mypy + run: | + mypy src/ + + - name: Check formatting with black + run: | + black --check src/ + + - name: Check imports with isort + run: | + isort --check-only src/ + + - name: Run tests with pytest + env: + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/peikarband_test + REDIS_URL: redis://localhost:6379/0 + SECRET_KEY: test-secret-key + JWT_SECRET_KEY: test-jwt-secret + CELERY_BROKER_URL: redis://localhost:6379/1 + CELERY_RESULT_BACKEND: redis://localhost:6379/2 + run: | + pytest tests/ -v --cov=src --cov-report=xml --cov-report=term-missing + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + fail_ci_if_error: false + + security: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install bandit safety + + - name: Run Bandit security scan + run: | + bandit -r src/ -f json -o bandit-report.json || true + + - name: Run Safety check + run: | + safety check --json || true + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb797cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +venv/ +env/ +ENV/ + +# Reflex +.web/ +.reflex/ +.states/ +reflex.db +reflex.db-* + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Testing +.pytest_cache/ +.coverage +htmlcov/ +.tox/ + +# Logs +*.log +logs/ + +# Environment +.env +.env.local +.env.*.local + +# Temporary +tmp/ +temp/ +*.tmp + +# Docker +.dockerignore + +# Kubernetes secrets +*secret*.yaml +*Secret*.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a547e89 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,43 @@ +# Pre-commit hooks for Peikarband +# Install: pre-commit install +# Run manually: pre-commit run --all-files + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: check-json + - id: check-toml + - id: check-merge-conflict + - id: debug-statements + - id: mixed-line-ending + + - repo: https://github.com/psf/black + rev: 23.12.0 + hooks: + - id: black + language_version: python3.11 + + - repo: https://github.com/PyCQA/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "black"] + + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + args: ["--max-line-length=120", "--extend-ignore=E203,W503"] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.7.1 + hooks: + - id: mypy + additional_dependencies: [types-all] + args: ["--config-file=mypy.ini"] + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7671bac --- /dev/null +++ b/Dockerfile @@ -0,0 +1,71 @@ +# Peikarband Platform - Production Dockerfile +# Multi-stage build for optimized image size + +# Stage 1: Builder +FROM python:3.11-slim as builder + +WORKDIR /build + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + curl \ + gnupg \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js (required for Reflex) +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir --user -r requirements.txt + +# Copy application code +COPY . . + +# Initialize and build Reflex app +RUN python -m reflex init --template blank && \ + python -m reflex export --frontend-only --no-zip + +# Stage 2: Runtime +FROM python:3.11-slim + +WORKDIR /app + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + postgresql-client \ + curl \ + nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Copy Python dependencies from builder +COPY --from=builder /root/.local /root/.local + +# Copy application and built assets +COPY --from=builder /build /app + +# Create non-root user +RUN useradd -m -u 1000 peikarband && \ + chown -R peikarband:peikarband /app + +# Set environment variables +ENV PATH=/root/.local/bin:$PATH \ + PYTHONUNBUFFERED=1 \ + REFLEX_ENV=production + +USER peikarband + +# Expose ports (backend: 8000, frontend: 3000) +EXPOSE 3000 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:8000/ping || exit 1 + +# Run application +CMD ["python", "-m", "reflex", "run", "--env", "production", "--backend-only"] + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..630dc97 --- /dev/null +++ b/Makefile @@ -0,0 +1,126 @@ +# Peikarband Platform - Makefile + +REGISTRY ?= registry.example.com +IMAGE_NAME ?= peikarband/landing +VERSION ?= $(shell git describe --tags --always --dirty) +HELM_RELEASE ?= peikarband +NAMESPACE ?= production + +.PHONY: help install dev test lint format clean docker-up docker-down migrate + +help: + @echo "Available commands:" + @echo "" + @echo "Development:" + @echo " make install - Install dependencies" + @echo " make dev - Run development server" + @echo " make test - Run tests" + @echo " make lint - Run linters" + @echo " make format - Format code" + @echo " make clean - Clean temporary files" + @echo "" + @echo "Docker:" + @echo " make docker-build - Build Docker image" + @echo " make docker-push - Push Docker image" + @echo " make docker-up - Start Docker Compose" + @echo " make docker-down - Stop Docker Compose" + @echo "" + @echo "Kubernetes/Helm:" + @echo " make helm-lint - Lint Helm chart" + @echo " make helm-package - Package Helm chart" + @echo " make helm-install - Install Helm chart" + @echo " make helm-upgrade - Upgrade Helm chart" + @echo " make helm-uninstall - Uninstall Helm chart" + @echo " make k8s-deploy - Deploy to Kubernetes" + @echo "" + @echo "Database:" + @echo " make migrate - Run database migrations" + +install: + pip install -r requirements.txt + pip install -r requirements-dev.txt + pre-commit install + +dev: + python -m reflex run + +test: + pytest tests/ -v --cov=src --cov-report=html + +lint: + flake8 src/ + mypy src/ + black --check src/ + isort --check-only src/ + +format: + black src/ + isort src/ + +clean: + find . -type d -name "__pycache__" -exec rm -rf {} + + find . -type f -name "*.pyc" -delete + find . -type f -name "*.pyo" -delete + find . -type d -name ".pytest_cache" -exec rm -rf {} + + find . -type d -name ".mypy_cache" -exec rm -rf {} + + find . -type d -name "*.egg-info" -exec rm -rf {} + + rm -rf .coverage htmlcov/ + rm -rf dist/ build/ + +# Docker commands +docker-build: + docker build -t $(IMAGE_NAME):$(VERSION) . + docker tag $(IMAGE_NAME):$(VERSION) $(IMAGE_NAME):latest + +docker-push: + docker tag $(IMAGE_NAME):$(VERSION) $(REGISTRY)/$(IMAGE_NAME):$(VERSION) + docker tag $(IMAGE_NAME):$(VERSION) $(REGISTRY)/$(IMAGE_NAME):latest + docker push $(REGISTRY)/$(IMAGE_NAME):$(VERSION) + docker push $(REGISTRY)/$(IMAGE_NAME):latest + +docker-up: + docker-compose up -d + +docker-down: + docker-compose down + +# Helm commands +helm-lint: + helm lint helm/peikarband + +helm-template: + helm template $(HELM_RELEASE) helm/peikarband --debug + +helm-package: + helm package helm/peikarband --destination . + +helm-install: + helm install $(HELM_RELEASE) helm/peikarband \ + --namespace $(NAMESPACE) \ + --create-namespace \ + --set image.repository=$(REGISTRY)/$(IMAGE_NAME) \ + --set image.tag=$(VERSION) \ + --wait + +helm-upgrade: + helm upgrade --install $(HELM_RELEASE) helm/peikarband \ + --namespace $(NAMESPACE) \ + --set image.repository=$(REGISTRY)/$(IMAGE_NAME) \ + --set image.tag=$(VERSION) \ + --wait + +helm-uninstall: + helm uninstall $(HELM_RELEASE) --namespace $(NAMESPACE) + +# Kubernetes deployment (full pipeline) +k8s-deploy: docker-build docker-push helm-upgrade + @echo "Deployment complete!" + @echo "Check status: kubectl get pods -n $(NAMESPACE)" + +# Database +migrate: + alembic upgrade head + +seed: + python scripts/seed_database.py + diff --git a/README.md b/README.md new file mode 100644 index 0000000..339e857 --- /dev/null +++ b/README.md @@ -0,0 +1,217 @@ +# پیکربند - پلتفرم جامع مدیریت هاستینگ و زیرساخت ابری + +## 📖 درباره پروژه + +پیکربند یک پلتفرم حرفه‌ای برای مدیریت هاستینگ، سرورهای ابری، دامین و خدمات DevOps است. این پلتفرم با الهام از سرویس‌هایی مانند Cloudways، DigitalOcean و پارس پک طراحی شده است. + +## 🏗️ معماری + +این پروژه بر اساس **Clean Architecture** و اصول **SOLID** طراحی شده است: + +- **Domain Layer**: منطق کسب‌وکار اصلی +- **Application Layer**: موارد استفاده (Use Cases) +- **Infrastructure Layer**: پیاده‌سازی‌های فنی +- **Presentation Layer**: رابط کاربری (Reflex) + +## 🚀 تکنولوژی‌ها + +- **Frontend/Backend**: Python Reflex +- **Database**: PostgreSQL + SQLAlchemy +- **Cache**: Redis +- **Task Queue**: Celery +- **Testing**: pytest +- **Code Quality**: black, flake8, mypy, isort + +## 📋 پیش‌نیازها + +- Python 3.11+ +- PostgreSQL 14+ +- Redis 7+ +- Node.js 18+ (برای Reflex) + +## 🛠️ نصب و راه‌اندازی + +### 1. کلون کردن پروژه + +```bash +git clone https://github.com/yourusername/peikarband.git +cd peikarband +``` + +### 2. ایجاد محیط مجازی + +```bash +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +### 3. نصب وابستگی‌ها + +```bash +pip install -r requirements.txt +pip install -r requirements-dev.txt # برای توسعه +``` + +### 4. تنظیم Environment Variables + +```bash +cp .env.example .env +# ویرایش .env و تکمیل مقادیر +``` + +### 5. راه‌اندازی دیتابیس + +```bash +# ایجاد دیتابیس +createdb peikarband + +# اجرای migrations +alembic upgrade head +``` + +### 6. اجرای پروژه + +```bash +# توسعه +python -m reflex run + +# یا +make dev +``` + +## 🚢 Deployment + +### با Docker + +```bash +# Build +docker build -t peikarband:latest . + +# Run +docker-compose up -d +``` + +### با Kubernetes/Helm + +```bash +# Deploy +helm upgrade --install peikarband ./helm/peikarband \ + --namespace production \ + --set image.tag=0.1.0 + +# یا +make k8s-deploy +``` + +📖 [راهنمای کامل Deployment](docs/deployment/kubernetes.md) + +## 📁 ساختار پروژه + +``` +peikarband/ +├── docs/ # مستندات +├── src/ +│ ├── config/ # تنظیمات +│ ├── core/ # هسته اصلی +│ │ ├── domain/ # Domain entities & logic +│ │ └── application/ # Use cases & DTOs +│ ├── infrastructure/ # پیاده‌سازی‌های فنی +│ ├── presentation/ # رابط کاربری +│ └── shared/ # کدهای مشترک +├── tests/ # تست‌ها +└── scripts/ # اسکریپت‌های کمکی +``` + +## 🧪 تست + +```bash +# اجرای همه تست‌ها +pytest + +# با coverage +pytest --cov=src tests/ + +# تست‌های خاص +pytest tests/unit/ +pytest tests/integration/ +``` + +## 📝 کدنویسی + +### استانداردها + +- **PEP 8**: استاندارد کدنویسی Python +- **PEP 20**: Zen of Python +- **Type Hints**: همه جا استفاده شود +- **Docstrings**: Google Style + +### ابزارهای کیفیت کد + +```bash +# Format +black src/ + +# Linting +flake8 src/ + +# Type checking +mypy src/ + +# Import sorting +isort src/ +``` + +### Pre-commit Hooks + +```bash +pre-commit install +pre-commit run --all-files +``` + +## 📚 مستندات + +مستندات کامل در پوشه `docs/` موجود است: + +- [Handbook](docs/handbook.md): راهنمای جامع پروژه +- [Architecture](docs/architecture/): معماری سیستم +- [Development](docs/development/): راهنمای توسعه +- [API Reference](docs/api/): مستندات API + +## 🔐 امنیت + +- همه پسوردها با bcrypt hash می‌شوند +- استفاده از JWT برای authentication +- پشتیبانی از 2FA +- اطلاعات حساس رمزنگاری می‌شوند + +## 🤝 مشارکت + +برای مشارکت در پروژه: + +1. Fork کنید +2. Branch جدید بسازید (`git checkout -b feature/amazing-feature`) +3. Commit کنید (`git commit -m 'feat: add amazing feature'`) +4. Push کنید (`git push origin feature/amazing-feature`) +5. Pull Request بسازید + +## 📄 لایسنس + +این پروژه تحت لایسنس MIT منتشر شده است. + +## 👥 تیم + +- Lead Developer: [Your Name] +- Architecture: Clean Architecture +- Methodology: Agile/Scrum + +## 📞 تماس + +- Website: https://peikarband.ir +- Email: support@peikarband.ir +- Telegram: @peikarband + +--- + +**نسخه**: 0.1.0 +**آخرین بروزرسانی**: 2025-01-24 + diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..fd16b85 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,102 @@ +# Alembic configuration file + +[alembic] +# path to migration scripts +script_location = src/infrastructure/database/migrations + +# template used to generate migration files +file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = postgresql://username:password@localhost/peikarband + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S + diff --git a/argocd/README.md b/argocd/README.md new file mode 100644 index 0000000..9c59d0f --- /dev/null +++ b/argocd/README.md @@ -0,0 +1,154 @@ +# ArgoCD Deployment + +This directory contains ArgoCD Application manifests for deploying Peikarband to Kubernetes. + +## Files + +- `application.yaml`: Production deployment (main branch → peikarband namespace) +- `application-staging.yaml`: Staging deployment (develop branch → peikarband-staging namespace) + +## Prerequisites + +1. ArgoCD installed in your cluster +2. Git repository access configured in ArgoCD +3. Docker registry credentials (if using private registry) + +## Deployment + +### 1. Add Git Repository to ArgoCD + +```bash +# For SSH +argocd repo add git@git.peikarband.ir:ehsan-minadd/peikarband.git \ + --ssh-private-key-path ~/.ssh/id_rsa \ + --insecure-skip-server-verification + +# Or using argocd UI: Settings → Repositories → Connect Repo +``` + +### 2. Deploy Production + +```bash +kubectl apply -f argocd/application.yaml +``` + +### 3. Deploy Staging + +```bash +kubectl apply -f argocd/application-staging.yaml +``` + +## Sync Policy + +Both applications use **automatic sync** with: +- **Auto-prune**: Remove resources deleted from Git +- **Self-heal**: Automatically sync when cluster state differs from Git +- **Retry logic**: 5 attempts with exponential backoff + +## Monitoring + +```bash +# Check application status +argocd app get peikarband +argocd app get peikarband-staging + +# Watch sync progress +argocd app sync peikarband --watch + +# View logs +argocd app logs peikarband +``` + +## Manual Sync + +```bash +# Force sync +argocd app sync peikarband --force + +# Sync with prune +argocd app sync peikarband --prune +``` + +## Rollback + +```bash +# List history +argocd app history peikarband + +# Rollback to specific revision +argocd app rollback peikarband +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────┐ +│ ArgoCD │ +│ ┌───────────────────┐ ┌──────────────────┐ │ +│ │ Production App │ │ Staging App │ │ +│ │ (main branch) │ │ (develop branch) │ │ +│ └─────────┬─────────┘ └────────┬─────────┘ │ +└────────────┼─────────────────────┼──────────────┘ + │ │ + ▼ ▼ + ┌────────────────┐ ┌─────────────────┐ + │ namespace: │ │ namespace: │ + │ peikarband │ │ peikarband-stg │ + └────────────────┘ └─────────────────┘ +``` + +## Environment Variables + +Override via Helm values: + +```yaml +# In values-production.yaml or values-staging.yaml +env: + - name: DATABASE_URL + value: "postgresql://..." + - name: REDIS_URL + value: "redis://..." +``` + +## Secrets Management + +Secrets should be managed outside Git: + +```bash +# Using kubectl +kubectl create secret generic peikarband-secrets \ + --from-literal=database-password=xxx \ + --namespace=peikarband + +# Or using Sealed Secrets, External Secrets Operator, etc. +``` + +## Troubleshooting + +### Application Out of Sync + +```bash +argocd app sync peikarband --force +``` + +### Image Pull Errors + +Check registry credentials: +```bash +kubectl get secret regcred -n peikarband -o yaml +``` + +### Health Check Failing + +View pod logs: +```bash +kubectl logs -n peikarband -l app=peikarband --tail=100 +``` + +### Helm Values Override Not Working + +Verify values file path in Application manifest: +```bash +argocd app manifests peikarband | grep valueFiles +``` + diff --git a/argocd/application-staging.yaml b/argocd/application-staging.yaml new file mode 100644 index 0000000..e668877 --- /dev/null +++ b/argocd/application-staging.yaml @@ -0,0 +1,47 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: peikarband-staging + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + source: + repoURL: git@git.peikarband.ir:ehsan-minadd/peikarband.git + targetRevision: develop + path: helm/peikarband + helm: + releaseName: peikarband-staging + valueFiles: + - values-staging.yaml + + destination: + server: https://kubernetes.default.svc + namespace: peikarband-staging + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + + revisionHistoryLimit: 10 + + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + diff --git a/argocd/application.yaml b/argocd/application.yaml new file mode 100644 index 0000000..d9f8008 --- /dev/null +++ b/argocd/application.yaml @@ -0,0 +1,47 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: peikarband + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + source: + repoURL: git@git.peikarband.ir:ehsan-minadd/peikarband.git + targetRevision: HEAD + path: helm/peikarband + helm: + releaseName: peikarband + valueFiles: + - values-production.yaml + + destination: + server: https://kubernetes.default.svc + namespace: peikarband + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + + revisionHistoryLimit: 10 + + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + diff --git a/assets/custom.css b/assets/custom.css new file mode 100644 index 0000000..0c4f272 --- /dev/null +++ b/assets/custom.css @@ -0,0 +1,31 @@ +@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100..900&display=swap'); + +body { + font-family: 'Vazirmatn', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +@keyframes gradientShift { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +@keyframes glow { + 0% { box-shadow: 0 12px 40px rgba(30, 64, 175, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.2); } + 50% { box-shadow: 0 18px 50px rgba(30, 64, 175, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.3); } + 100% { box-shadow: 0 12px 40px rgba(30, 64, 175, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.2); } +} + +@keyframes pulse { + 0% { transform: scale(1); opacity: 1; } + 50% { transform: scale(1.05); opacity: 0.8; } + 100% { transform: scale(1); opacity: 1; } +} + +@keyframes float { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-20px); } +} + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f424bf5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,92 @@ +version: '3.8' + +services: + # PostgreSQL Database + postgres: + image: postgres:14-alpine + container_name: peikarband-db + environment: + POSTGRES_USER: ${DB_USER:-peikarband} + POSTGRES_PASSWORD: ${DB_PASSWORD:-peikarband} + POSTGRES_DB: ${DB_NAME:-peikarband} + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U peikarband"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis Cache + redis: + image: redis:7-alpine + container_name: peikarband-redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Peikarband Application + app: + build: . + container_name: peikarband-app + depends_on: + - postgres + - redis + ports: + - "3000:3000" + - "8000:8000" + environment: + - DATABASE_URL=postgresql://peikarband:peikarband@postgres:5432/peikarband + - REDIS_URL=redis://redis:6379/0 + - CELERY_BROKER_URL=redis://redis:6379/1 + - CELERY_RESULT_BACKEND=redis://redis:6379/2 + - SECRET_KEY=${SECRET_KEY} + - JWT_SECRET_KEY=${JWT_SECRET_KEY} + - ENVIRONMENT=production + volumes: + - ./:/app + restart: unless-stopped + + # Celery Worker + celery: + build: . + container_name: peikarband-celery + command: celery -A src.infrastructure.tasks.celery_app worker -l info + depends_on: + - postgres + - redis + environment: + - DATABASE_URL=postgresql://peikarband:peikarband@postgres:5432/peikarband + - REDIS_URL=redis://redis:6379/0 + - CELERY_BROKER_URL=redis://redis:6379/1 + - CELERY_RESULT_BACKEND=redis://redis:6379/2 + volumes: + - ./:/app + restart: unless-stopped + + # Flower (Celery Monitoring) + flower: + build: . + container_name: peikarband-flower + command: celery -A src.infrastructure.tasks.celery_app flower + depends_on: + - redis + - celery + ports: + - "5555:5555" + environment: + - CELERY_BROKER_URL=redis://redis:6379/1 + restart: unless-stopped + +volumes: + postgres_data: + redis_data: + diff --git a/docs/architecture/database-strategy.md b/docs/architecture/database-strategy.md new file mode 100644 index 0000000..ff5ab3c --- /dev/null +++ b/docs/architecture/database-strategy.md @@ -0,0 +1,453 @@ +# استراتژی دیتابیس - انعطاف و مقیاس‌پذیری + +## مشکل: انعطاف vs ثبات + +با توجه به اینکه پروژه قرار است: +- کلی feature جدید بگیره +- Options و configurations متنوع داشته باشه +- قابل توسعه سریع باشه + +آیا PostgreSQL محدودیت ایجاد می‌کنه؟ + +## ✅ راه حل: Hybrid Database Architecture + +### لایه‌بندی داده بر اساس نوع: + +``` +┌──────────────────────────────────────────────┐ +│ Application Layer │ +└────┬─────────────┬──────────────┬────────────┘ + │ │ │ +┌────▼─────┐ ┌───▼────┐ ┌───▼────┐ +│PostgreSQL│ │MongoDB │ │ Redis │ +│ │ │ │ │ │ +│Critical │ │Flexible│ │ Cache │ +│Structured│ │Dynamic │ │Session │ +└──────────┘ └────────┘ └────────┘ +``` + +### Tier 1: PostgreSQL - Critical Business Data + +**چی بریزیم توی PostgreSQL:** +- User accounts +- Financial data (invoices, transactions, wallet) +- Service subscriptions +- Audit logs +- Anything که نیاز به ACID داره + +```python +# ❌ این‌ها رو NEVER توی MongoDB نذاریم +class Invoice(BaseModel): + id: int + user_id: int + amount: Decimal # MUST be ACID + status: str + paid_at: datetime +``` + +**چرا PostgreSQL؟** +- ✅ ACID Transactions +- ✅ Data Integrity +- ✅ Complex Joins +- ✅ Proven for financial data + +### Tier 2: PostgreSQL JSONB - Flexible Structured + +**چی بریزیم توی JSONB:** +- Service configurations +- User preferences +- Feature flags +- Custom metadata +- Plugin settings + +```python +from sqlalchemy.dialects.postgresql import JSONB + +class Service(BaseModel): + # Structured + id = Column(Integer, primary_key=True) + user_id = Column(Integer) + type = Column(String) # 'vps', 'hosting', etc. + + # Flexible - هر چیزی می‌تونه باشه! + config = Column(JSONB, default={}) + metadata = Column(JSONB, default={}) + +# Example 1: VPS +vps = Service( + type="vps", + config={ + "cpu": 4, + "ram": 8, + "disk": 100, + "os": "ubuntu-22.04" + } +) + +# Example 2: WordPress Hosting +wp = Service( + type="wordpress", + config={ + "domain": "example.com", + "php_version": "8.2", + "auto_update": True, + "cdn_enabled": True, + # Future features - بدون migration! + "new_feature_2025": {"enabled": True} + } +) +``` + +**Query کردن JSONB:** + +```python +# پیدا کردن VPS های بالای 4 CPU +services = session.query(Service).filter( + Service.config['cpu'].astext.cast(Integer) >= 4 +).all() + +# پیدا کردن WordPress با CDN enabled +services = session.query(Service).filter( + Service.config['cdn_enabled'].astext == 'true' +).all() + +# Index روی JSONB برای performance +from sqlalchemy import Index + +Index( + 'idx_service_config_cpu', + Service.config['cpu'].astext.cast(Integer) +) +``` + +### Tier 3: MongoDB - Completely Dynamic + +**چی بریزیم توی MongoDB:** +- Logs و events +- Analytics data +- User activity tracking +- System metrics +- Unstructured data + +```python +# MongoDB - No schema! +{ + "_id": "service_log_123", + "service_id": 123, + "timestamp": "2025-01-24T...", + "events": [ + { + "type": "cpu_spike", + "value": 95, + "custom_field_1": "...", + # هر چیزی که بخوایم! + } + ], + "metrics": { + # Structure آزاد + "anything": "goes here" + } +} +``` + +## پیاده‌سازی در کد + +### 1. Service با JSONB: + +```python +# src/infrastructure/database/models/service_model.py + +from sqlalchemy import Column, Integer, String, ForeignKey, Enum as SQLEnum +from sqlalchemy.dialects.postgresql import JSONB +from sqlalchemy.orm import relationship +import enum + +class ServiceType(enum.Enum): + VPS = "vps" + HOSTING = "hosting" + WORDPRESS = "wordpress" + DOMAIN = "domain" + +class ServiceModel(BaseModel): + __tablename__ = "services" + + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('users.id'), nullable=False) + type = Column(SQLEnum(ServiceType), nullable=False) + status = Column(String(20), nullable=False) + + # Flexible configuration + config = Column(JSONB, nullable=False, default={}) + metadata = Column(JSONB, nullable=False, default={}) + + # Relationships + user = relationship("UserModel", back_populates="services") +``` + +### 2. Repository با JSONB support: + +```python +# src/infrastructure/database/repositories/service_repository.py + +from typing import Dict, Any, List +from sqlalchemy.orm import Session + +class ServiceRepository: + def __init__(self, session: Session): + self._session = session + + def find_by_config( + self, + json_path: str, + value: Any + ) -> List[ServiceModel]: + """Query by JSONB field. + + Example: + repo.find_by_config('cpu', 4) + repo.find_by_config('cdn_enabled', True) + """ + return self._session.query(ServiceModel).filter( + ServiceModel.config[json_path].astext == str(value) + ).all() + + def update_config( + self, + service_id: int, + config_updates: Dict[str, Any] + ) -> ServiceModel: + """Update service config partially.""" + service = self.get_by_id(service_id) + if service: + # Merge configs + new_config = {**service.config, **config_updates} + service.config = new_config + self._session.commit() + return service +``` + +### 3. Domain Entity که flexible هست: + +```python +# src/core/domain/entities/service.py + +from typing import Dict, Any, Optional +from src.core.domain.entities.base import BaseEntity + +class Service(BaseEntity): + """Service domain entity with flexible configuration.""" + + def __init__( + self, + id: Optional[int] = None, + user_id: int = None, + type: str = None, + status: str = None, + config: Dict[str, Any] = None, + metadata: Dict[str, Any] = None, + **kwargs + ): + super().__init__(id=id, **kwargs) + self.user_id = user_id + self.type = type + self.status = status + self.config = config or {} + self.metadata = metadata or {} + + def get_config(self, key: str, default: Any = None) -> Any: + """Get config value safely.""" + return self.config.get(key, default) + + def set_config(self, key: str, value: Any) -> None: + """Set config value.""" + self.config[key] = value + + def update_config(self, updates: Dict[str, Any]) -> None: + """Update multiple config values.""" + self.config.update(updates) +``` + +## مقایسه روش‌ها + +### Migration-based (Traditional SQL) + +```python +# ❌ هر feature جدید = migration جدید + +# Migration 001 +ALTER TABLE services ADD COLUMN cpu INTEGER; + +# Migration 002 +ALTER TABLE services ADD COLUMN ram INTEGER; + +# Migration 003 +ALTER TABLE services ADD COLUMN new_feature VARCHAR(255); + +# بعد از 100 feature = 100 migration! 😱 +``` + +### JSONB-based (Flexible SQL) + +```python +# ✅ هیچ migration لازم نیست! + +# روز اول +service.config = {"cpu": 4} + +# یک ماه بعد +service.config = {"cpu": 4, "ram": 8} + +# یک سال بعد +service.config = { + "cpu": 4, + "ram": 8, + "new_feature_2025": True, + "another_feature": {"nested": "data"} +} + +# بدون هیچ migration! 🎉 +``` + +## Best Practices + +### 1. تصمیم‌گیری: PostgreSQL vs MongoDB vs JSONB + +```python +# PostgreSQL (Structured) +✅ User authentication +✅ Financial transactions +✅ Invoices +✅ Core business entities + +# PostgreSQL JSONB +✅ Service configurations +✅ User preferences +✅ Feature flags +✅ Plugin settings +✅ Custom fields + +# MongoDB +✅ Logs & Events +✅ Analytics +✅ User activity +✅ System metrics +✅ Temporary data +``` + +### 2. JSONB Schema Validation (در Application) + +```python +from pydantic import BaseModel +from typing import Optional + +class VPSConfig(BaseModel): + """Schema for VPS service config.""" + cpu: int + ram: int + disk: int + os: str + backups: Optional[bool] = False + +class WordPressConfig(BaseModel): + """Schema for WordPress service config.""" + domain: str + php_version: str + auto_update: bool + cdn_enabled: Optional[bool] = False + +# Validation +def validate_service_config(service_type: str, config: dict): + """Validate config based on service type.""" + schemas = { + "vps": VPSConfig, + "wordpress": WordPressConfig, + } + schema = schemas.get(service_type) + if schema: + return schema(**config) # Validates + return config +``` + +### 3. Indexing برای Performance + +```python +# در migration +from alembic import op + +def upgrade(): + # Create GIN index on JSONB + op.execute(""" + CREATE INDEX idx_services_config_gin + ON services USING GIN (config); + """) + + # Index specific keys + op.execute(""" + CREATE INDEX idx_services_config_cpu + ON services ((config->>'cpu')); + """) +``` + +## مزایا و معایب + +### PostgreSQL + JSONB (پیشنهاد من) + +**مزایا:** +- ✅ انعطاف بالا (مثل MongoDB) +- ✅ ACID transactions (برای billing امن) +- ✅ Query قدرتمند +- ✅ یک database کمتر = ساده‌تر +- ✅ Performance خوب با indexing +- ✅ Data integrity + +**معایب:** +- ❌ JSONB query ها کمی پیچیده‌تر از SQL معمولی +- ❌ نیاز به validation در application layer + +### Hybrid (PostgreSQL + MongoDB) + +**مزایا:** +- ✅ Best of both worlds +- ✅ Separation of concerns +- ✅ Optimal performance + +**معایب:** +- ❌ پیچیدگی بیشتر +- ❌ دو database = maintenance بیشتر +- ❌ Consistency بین دو DB + +## نتیجه‌گیری + +**پیشنهاد برای پیکربند:** + +```python +""" +Tier 1: PostgreSQL - Critical + - Users, Auth + - Invoices, Transactions + - Wallet, Payments + +Tier 2: PostgreSQL JSONB - Flexible + - Service configs + - User preferences + - Custom settings + +Tier 3: Redis - Cache + - Sessions + - Cache + - Rate limiting + +(Optional) Tier 4: MongoDB - Logs + - Activity logs + - System metrics + - Analytics +""" +``` + +**در عمل:** +- شروع با PostgreSQL + JSONB +- اگر لازم شد، MongoDB اضافه می‌کنیم +- ساده، flexible، و قابل توسعه + +**PostgreSQL دست و پای ما رو نمی‌بنده، اگر از JSONB استفاده کنیم!** ✨ + diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..896911b --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,174 @@ +# معماری کلی سیستم + +## نمای کلی + +پلتفرم پیکربند بر اساس **Clean Architecture** طراحی شده که قابلیت تست، نگهداری و توسعه را به حداکثر می‌رساند. + +## لایه‌های معماری + +### 1. Domain Layer (هسته مرکزی) + +مستقل‌ترین لایه که شامل منطق کسب‌وکار خالص است: + +**Components:** +- **Entities**: موجودیت‌های اصلی (User, Service, Invoice, Server) +- **Value Objects**: Email, Money, Phone, IPAddress +- **Domain Services**: منطق پیچیده‌ای که به چند entity مرتبط است +- **Domain Events**: رویدادهای کسب‌وکار +- **Exceptions**: خطاهای دامین + +**قوانین:** +- هیچ وابستگی به لایه‌های دیگر ندارد +- فقط منطق کسب‌وکار +- بدون وابستگی به framework +- Pure Python + +### 2. Application Layer (موارد استفاده) + +**Components:** +- **Use Cases**: موارد استفاده سیستم (RegisterUser, CreateInvoice) +- **DTOs**: Data Transfer Objects +- **Interfaces**: تعریف رابط‌های سرویس‌ها +- **Validators**: اعتبارسنجی ورودی‌ها + +**قوانین:** +- وابسته به Domain Layer +- مستقل از Infrastructure +- تعریف رابط‌های مورد نیاز + +### 3. Infrastructure Layer (جزئیات فنی) + +**Components:** +- **Database**: PostgreSQL + SQLAlchemy +- **Cache**: Redis +- **External APIs**: DigitalOcean, Hetzner, OVH +- **Tasks**: Celery background jobs +- **Security**: Authentication, Authorization +- **Logging**: Structured logging + +**قوانین:** +- پیاده‌سازی interface های Application Layer +- وابسته به تکنولوژی‌های خاص +- قابل تعویض + +### 4. Presentation Layer (رابط کاربری) + +**Components:** +- **Web**: Reflex pages و components +- **API**: REST endpoints (optional) +- **State Management**: Reflex states + +**قوانین:** +- فقط به Application Layer وابسته +- مستقل از Infrastructure details + +## جریان داده + +``` +User Action + ↓ +Presentation Layer (Reflex Component) + ↓ +Application Layer (Use Case) + ↓ +Domain Layer (Business Logic) + ↓ +Application Layer (Interfaces) + ↓ +Infrastructure Layer (Implementation) + ↓ +External Systems (Database, APIs) +``` + +## Dependency Rule + +وابستگی‌ها همیشه به سمت داخل (به سمت Domain) هستند: + +``` +Presentation → Application → Domain +Infrastructure → Application → Domain +``` + +**قانون طلایی**: لایه‌های داخلی هیچ چیز از لایه‌های خارجی نمی‌دانند. + +## مزایای این معماری + +1. **Testability**: هر لایه مستقلا قابل تست +2. **Maintainability**: تغییرات محلی و جداسازی شده +3. **Flexibility**: تعویض آسان تکنولوژی‌ها +4. **Scalability**: قابل مقیاس‌پذیری در هر لایه +5. **Business Logic First**: تمرکز روی منطق کسب‌وکار + +## Domain-Driven Design (DDD) + +پروژه از اصول DDD استفاده می‌کند: + +- **Ubiquitous Language**: زبان مشترک با کسب‌وکار +- **Bounded Contexts**: محدوده‌های مشخص +- **Aggregates**: مجموعه‌های یکپارچه +- **Repositories**: دسترسی به داده +- **Domain Events**: رویدادهای کسب‌وکار + +## Technology Stack + +### Core +- **Language**: Python 3.11+ +- **Framework**: Reflex 0.4.0 +- **Database**: PostgreSQL 14+ +- **Cache**: Redis 7+ +- **ORM**: SQLAlchemy 2.0+ + +### Infrastructure +- **Task Queue**: Celery 5.3+ +- **Testing**: pytest 7.4+ +- **Logging**: structlog +- **API Client**: httpx + +### External Services +- **Cloud Providers**: DigitalOcean, Hetzner, OVH +- **Payment**: Zarinpal, IDPay +- **Monitoring**: Sentry, Prometheus + +## Security Architecture + +- **Authentication**: JWT + 2FA +- **Authorization**: RBAC (Role-Based Access Control) +- **Encryption**: Data at rest & in transit +- **Secrets Management**: Environment variables +- **Audit Logging**: تمام اقدامات مهم + +## Scalability Strategy + +### Horizontal Scaling +- Load balancing برای Reflex +- Database replication +- Redis clustering +- Celery workers + +### Vertical Scaling +- Connection pooling +- Query optimization +- Caching strategy +- Async operations + +## Monitoring & Observability + +- **Metrics**: Request rate, response time, error rate +- **Logs**: Structured logging با contextual info +- **Tracing**: Request tracing +- **Alerts**: Critical issues +- **Dashboards**: Grafana/Prometheus + +## Future Considerations + +- Microservices architecture (در صورت نیاز) +- Event-driven architecture +- CQRS pattern +- GraphQL API +- Multi-tenancy + +--- + +**نسخه**: 1.0.0 +**آخرین بروزرسانی**: 2025-01-24 + diff --git a/docs/changelog/CHANGELOG.md b/docs/changelog/CHANGELOG.md new file mode 100644 index 0000000..ec05e66 --- /dev/null +++ b/docs/changelog/CHANGELOG.md @@ -0,0 +1,42 @@ +# Changelog + +تمام تغییرات قابل توجه این پروژه در این فایل مستند می‌شود. + +فرمت بر اساس [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) است، +و این پروژه از [Semantic Versioning](https://semver.org/spec/v2.0.0.html) پیروی می‌کند. + +## [Unreleased] + +### Added +- معماری اولیه پروژه بر اساس Clean Architecture +- ساختار پوشه‌های اصلی +- تنظیمات config با Pydantic +- Setup PostgreSQL و SQLAlchemy +- Setup Redis برای cache +- Structured logging با structlog +- pytest برای testing +- Pre-commit hooks +- مستندات جامع (Handbook) + +### Changed +- انتقال landing page به ساختار جدید + +## [0.1.0] - 2025-01-24 + +### Added +- شروع پروژه +- Landing page اولیه با Reflex +- رنگ‌بندی آبی مطابق لوگو +- بخش ویژه WordPress Cloud +- Badge "بزودی کنار شما خواهیم بود" +- فونت فارسی Vazirmatn + +### Design +- Hero section با gradient animations +- Service cards +- Pricing section +- Server comparison table +- Testimonials +- Contact form +- Floating chat button + diff --git a/docs/changelog/known-issues.md b/docs/changelog/known-issues.md new file mode 100644 index 0000000..a874724 --- /dev/null +++ b/docs/changelog/known-issues.md @@ -0,0 +1,69 @@ +# مشکلات شناخته شده (Known Issues) + +این فایل لیست مشکلات شناخته شده و workaround های موقت را نگه می‌دارد. + +## Format + +هر issue باید شامل: +- عنوان +- توضیحات +- تاریخ کشف +- Priority (Critical/High/Medium/Low) +- Status (Open/In Progress/Resolved) +- Workaround (اگر وجود دارد) +- Related tickets + +--- + +## فعلا مشکلی گزارش نشده + +پروژه در مراحل اولیه است و هنوز مشکل خاصی گزارش نشده. + +--- + +## Template برای Issue های آینده + +```markdown +## [ISSUE-001] عنوان مشکل + +**تاریخ کشف**: YYYY-MM-DD +**Priority**: Critical/High/Medium/Low +**Status**: Open/In Progress/Resolved +**نسخه**: X.Y.Z + +### توضیحات +توضیحات کامل مشکل + +### Steps to Reproduce +1. Step 1 +2. Step 2 +3. Step 3 + +### Expected Behavior +رفتار مورد انتظار + +### Actual Behavior +رفتار واقعی + +### Environment +- OS: Linux/Windows/Mac +- Python: 3.11 +- Database: PostgreSQL 14 + +### Workaround +راه حل موقت (اگر وجود دارد) + +### Related +- Ticket: #123 +- PR: #456 + +### Notes +یادداشت‌های اضافی +``` + +--- + +## مشکلات حل شده + +(لیست مشکلات حل شده به اینجا منتقل می‌شود) + diff --git a/docs/changelog/migrations.md b/docs/changelog/migrations.md new file mode 100644 index 0000000..d7d303b --- /dev/null +++ b/docs/changelog/migrations.md @@ -0,0 +1,65 @@ +# Database Migrations History + +این فایل تاریخچه تمام migration های دیتابیس را نگه می‌دارد. + +## Format + +هر migration باید شامل موارد زیر باشد: +- تاریخ +- نام فایل migration +- توضیحات کامل +- جداول/ستون‌های تغییر یافته +- وابستگی‌ها +- دستور rollback + +--- + +## Migrations + +### 2025-01-24: Initial Setup + +**Migration**: `pending` +**Description**: آماده‌سازی اولیه - هنوز migration اجرا نشده +**Status**: Pending +**Tables**: None yet +**Dependencies**: None +**Rollback**: N/A + +**Note**: اولین migration در فاز بعدی (phase0-database) ایجاد خواهد شد. + +--- + +## Template برای Migration های آینده + +```markdown +## YYYY-MM-DD: Title + +**Migration**: `XXX_description.py` +**Description**: توضیحات کامل تغییرات +**Status**: Applied / Pending / Rolled Back +**Tables**: +- table_name_1 +- table_name_2 + +**Changes**: +- Added column `column_name` to `table_name` +- Created table `new_table` +- Added index on `column_name` + +**Dependencies**: +- Previous migration: XXX + +**SQL Summary**: +```sql +ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN DEFAULT FALSE; +``` + +**Rollback**: +```bash +alembic downgrade -1 +``` + +**Tested**: Yes/No +**Production Deploy Date**: YYYY-MM-DD +``` + diff --git a/docs/deployment/kubernetes.md b/docs/deployment/kubernetes.md new file mode 100644 index 0000000..e3b415f --- /dev/null +++ b/docs/deployment/kubernetes.md @@ -0,0 +1,449 @@ +# راهنمای Deployment روی Kubernetes با Helm + +این راهنما نحوه deploy کردن پلتفرم Peikarband روی Kubernetes با استفاده از Helm Chart را توضیح می‌دهد. + +## پیش‌نیازها + +### 1. ابزارهای مورد نیاز + +```bash +# Kubectl (v1.24+) +kubectl version --client + +# Helm (v3.10+) +helm version + +# Docker (برای build local) +docker --version +``` + +### 2. دسترسی به Kubernetes Cluster + +```bash +# تست دسترسی +kubectl cluster-info +kubectl get nodes +``` + +### 3. Namespace ها + +```bash +# ساخت namespace ها +kubectl create namespace production +kubectl create namespace staging +``` + +## ساختار Helm Chart + +``` +helm/peikarband/ +├── Chart.yaml # Metadata +├── values.yaml # Default values +├── values-production.yaml # Production overrides +├── templates/ +│ ├── _helpers.tpl # Helper templates +│ ├── deployment.yaml # Deployment +│ ├── service.yaml # Service +│ ├── ingress.yaml # Ingress +│ ├── configmap.yaml # ConfigMap +│ ├── serviceaccount.yaml +│ ├── hpa.yaml # Horizontal Pod Autoscaler +│ ├── pdb.yaml # Pod Disruption Budget +│ ├── networkpolicy.yaml +│ └── NOTES.txt +└── .helmignore +``` + +## مراحل Deployment + +### 1. آماده‌سازی Secrets + +ابتدا باید secrets مورد نیاز را ایجاد کنید: + +```bash +# Database credentials +kubectl create secret generic peikarband-secrets \ + --from-literal=db-username=peikarband \ + --from-literal=db-password=STRONG_PASSWORD_HERE \ + --from-literal=redis-password=REDIS_PASSWORD_HERE \ + -n production + +# برای staging +kubectl create secret generic peikarband-secrets \ + --from-literal=db-username=peikarband \ + --from-literal=db-password=STAGING_PASSWORD \ + --from-literal=redis-password=REDIS_PASSWORD \ + -n staging +``` + +### 2. Build و Push Docker Image + +#### روش اول: با GitHub Actions (توصیه می‌شود) + +```bash +# فقط یک tag بزنید و GitHub Actions خودکار build و deploy می‌کند +git tag -a v0.1.0 -m "Release v0.1.0" +git push origin v0.1.0 +``` + +#### روش دوم: Build دستی + +```bash +# Build image +docker build -t peikarband/landing:0.1.0 . + +# Tag for registry +docker tag peikarband/landing:0.1.0 registry.example.com/peikarband/landing:0.1.0 + +# Push +docker push registry.example.com/peikarband/landing:0.1.0 +``` + +### 3. Validate Helm Chart + +قبل از deploy، chart را validate کنید: + +```bash +# Lint +helm lint helm/peikarband + +# Dry-run +helm install peikarband-test ./helm/peikarband \ + --dry-run \ + --debug \ + --namespace production + +# Template rendering +helm template peikarband ./helm/peikarband > rendered.yaml +``` + +### 4. Deploy به Staging + +```bash +helm upgrade --install peikarband-staging ./helm/peikarband \ + --namespace staging \ + --create-namespace \ + --set image.repository=registry.example.com/peikarband/landing \ + --set image.tag=0.1.0 \ + --set ingress.hosts[0].host=staging.peikarband.ir \ + --set replicaCount=2 \ + --wait \ + --timeout 5m +``` + +### 5. تست Staging + +```bash +# چک کردن pods +kubectl get pods -n staging + +# چک کردن logs +kubectl logs -f deployment/peikarband-staging -n staging + +# Port forward برای تست local +kubectl port-forward svc/peikarband-staging 3000:3000 -n staging + +# تست health check +curl http://localhost:8000/ping +``` + +### 6. Deploy به Production + +```bash +helm upgrade --install peikarband-prod ./helm/peikarband \ + --namespace production \ + --create-namespace \ + --set image.repository=registry.example.com/peikarband/landing \ + --set image.tag=0.1.0 \ + --values helm/peikarband/values-production.yaml \ + --wait \ + --timeout 10m +``` + +## پیکربندی‌های مهم + +### 1. تغییر تعداد Replicas + +```bash +# با Helm +helm upgrade peikarband-prod ./helm/peikarband \ + --namespace production \ + --reuse-values \ + --set replicaCount=5 + +# یا با kubectl +kubectl scale deployment peikarband-prod --replicas=5 -n production +``` + +### 2. Update Image Version + +```bash +helm upgrade peikarband-prod ./helm/peikarband \ + --namespace production \ + --reuse-values \ + --set image.tag=0.2.0 +``` + +### 3. تغییر Resources + +```bash +helm upgrade peikarband-prod ./helm/peikarband \ + --namespace production \ + --reuse-values \ + --set resources.limits.cpu=2000m \ + --set resources.limits.memory=2Gi +``` + +### 4. فعال/غیرفعال کردن Autoscaling + +```bash +# فعال کردن +helm upgrade peikarband-prod ./helm/peikarband \ + --namespace production \ + --reuse-values \ + --set autoscaling.enabled=true \ + --set autoscaling.minReplicas=3 \ + --set autoscaling.maxReplicas=10 + +# غیرفعال کردن +helm upgrade peikarband-prod ./helm/peikarband \ + --namespace production \ + --reuse-values \ + --set autoscaling.enabled=false \ + --set replicaCount=3 +``` + +## Ingress و SSL/TLS + +### نصب cert-manager (برای Let's Encrypt) + +```bash +# نصب cert-manager +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml + +# ساخت ClusterIssuer +cat < Decimal: + pass + +def send_welcome_email(user: User) -> bool: + pass + +# ❌ BAD +def calcTotPrice(): # Mixed case +def cp(): # Not descriptive +def calculate(): # Too vague +``` + +### Variables + +```python +# ✅ GOOD +user_email = "test@example.com" +total_amount = Decimal("100.00") +is_active = True +MAX_ATTEMPTS = 3 # Constant + +# ❌ BAD +e = "test@example.com" # Too short +userEmail = "test@example.com" # camelCase +TOTAL = 100 # Constant style for variable +``` + +## Type Hints + +**الزامی**: همه function signatures باید type hints داشته باشند. + +```python +from typing import Optional, List, Dict, Any +from decimal import Decimal + +# ✅ GOOD +def get_user(user_id: int) -> Optional[User]: + pass + +def create_invoice( + user_id: int, + amount: Decimal, + items: List[InvoiceItem] +) -> Invoice: + pass + +# ❌ BAD +def get_user(user_id): # No types + pass + +def create_invoice(user_id, amount, items): # No types + pass +``` + +## Docstrings + +**الزامی**: همه public functions, classes, و methods باید docstring داشته باشند. + +### Google Style (استاندارد پروژه) + +```python +def create_user( + email: str, + password: str, + full_name: str +) -> User: + """Create a new user account. + + Args: + email: User's email address + password: Plain text password + full_name: User's full name + + Returns: + User: Created user object + + Raises: + EmailAlreadyExistsException: If email exists + WeakPasswordException: If password is weak + + Example: + >>> user = create_user( + ... email="test@example.com", + ... password="SecurePass123!", + ... full_name="John Doe" + ... ) + """ + pass +``` + +## Error Handling + +### Use Specific Exceptions + +```python +# ✅ GOOD +try: + user = user_repository.get_by_id(user_id) + if not user: + raise UserNotFoundException(f"User {user_id} not found") +except DatabaseException as e: + logger.error("database_error", error=str(e)) + raise +except Exception as e: + logger.critical("unexpected_error", error=str(e)) + raise + +# ❌ BAD +try: + user = get_user(user_id) +except: # Never use bare except! + pass + +try: + user = get_user(user_id) +except Exception: # Too broad + pass +``` + +### Custom Exception Hierarchy + +```python +class PeikarbandException(Exception): + """Base exception.""" + pass + +class DomainException(PeikarbandException): + """Domain layer exceptions.""" + pass + +class UserNotFoundException(DomainException): + """User not found.""" + pass +``` + +## Code Organization + +### Imports + +```python +# 1. Standard library +import os +import sys +from typing import Optional, List +from decimal import Decimal + +# 2. Third-party +import redis +from sqlalchemy import Column, Integer +from pydantic import BaseModel + +# 3. Local +from src.config.settings import settings +from src.core.domain.entities.user import User +``` + +### Function Length + +**Max 50 lines per function** (توصیه: 20-30 lines) + +```python +# ✅ GOOD: Short and focused +def calculate_discount(amount: Decimal, user: User) -> Decimal: + """Calculate user discount.""" + if user.is_premium: + return amount * Decimal("0.10") + return Decimal("0.00") + +# ❌ BAD: Too long (100+ lines) +def process_order(order_data): + # 100+ lines of code + pass +``` + +### Class Length + +**Max 300 lines per class** (توصیه: 100-200 lines) + +اگر class بزرگ شد، به چند class کوچکتر تقسیم کنید. + +## Comments + +### When to Comment + +```python +# ✅ GOOD: Explain WHY, not WHAT +def calculate_tax(amount: Decimal) -> Decimal: + # Iranian tax rate is 9% as of 2024 + TAX_RATE = Decimal("0.09") + return amount * TAX_RATE + +# ❌ BAD: States the obvious +def add(a, b): + # Add two numbers + return a + b +``` + +### TODO Comments + +```python +# TODO(username): Description of what needs to be done +# TODO(john): Implement caching for this query +# FIXME(jane): This breaks when amount is negative +# HACK(bob): Temporary workaround until API is fixed +``` + +## Best Practices + +### 1. Single Responsibility + +```python +# ✅ GOOD +class UserRepository: + def save(self, user: User) -> User: + pass + +class EmailService: + def send_email(self, to: str, subject: str) -> bool: + pass + +# ❌ BAD +class UserManager: + def save_user(self, user): + pass + + def send_email(self, user): + pass + + def log_activity(self, user): + pass +``` + +### 2. DRY (Don't Repeat Yourself) + +```python +# ❌ BAD +def get_user_name(user_id): + db = connect_db() + user = db.query(User).filter_by(id=user_id).first() + db.close() + return user.name + +def get_user_email(user_id): + db = connect_db() + user = db.query(User).filter_by(id=user_id).first() + db.close() + return user.email + +# ✅ GOOD +def get_user(user_id: int) -> Optional[User]: + with get_db_context() as db: + return db.query(User).filter_by(id=user_id).first() + +def get_user_name(user_id: int) -> Optional[str]: + user = get_user(user_id) + return user.name if user else None +``` + +### 3. Early Return + +```python +# ✅ GOOD: Early return +def process_payment(amount: Decimal) -> bool: + if amount <= 0: + return False + + if not user.has_sufficient_balance(amount): + return False + + # Process payment + return True + +# ❌ BAD: Nested conditions +def process_payment(amount): + if amount > 0: + if user.has_sufficient_balance(amount): + # Process payment + return True + return False +``` + +### 4. Use Constants + +```python +# ✅ GOOD +MAX_LOGIN_ATTEMPTS = 3 +PASSWORD_MIN_LENGTH = 8 +SESSION_TIMEOUT_MINUTES = 30 + +if attempts >= MAX_LOGIN_ATTEMPTS: + lock_account() + +# ❌ BAD: Magic numbers +if attempts >= 3: # What's 3? + lock_account() +``` + +### 5. Avoid Deep Nesting + +```python +# ✅ GOOD: Max 2-3 levels +def process_order(order: Order) -> bool: + if not order.is_valid(): + return False + + if not user.can_order(): + return False + + return save_order(order) + +# ❌ BAD: Too nested +def process_order(order): + if order: + if order.is_valid(): + if user: + if user.can_order(): + if save_order(order): + return True + return False +``` + +## Code Review Checklist + +قبل از ارسال PR، این موارد را بررسی کنید: + +- [ ] همه تست‌ها pass می‌شوند +- [ ] Code coverage کافی است (>80%) +- [ ] تمام functions دارای type hints هستند +- [ ] تمام public functions دارای docstring هستند +- [ ] black, flake8, mypy بدون error +- [ ] هیچ TODO/FIXME جدید بدون توضیح نیست +- [ ] تغییرات در CHANGELOG.md ثبت شده +- [ ] مستندات به‌روز شده + +--- + +**الزامات**: این استانداردها الزامی هستند و در code review بررسی می‌شوند. + diff --git a/docs/development/git-workflow.md b/docs/development/git-workflow.md new file mode 100644 index 0000000..89ae5ea --- /dev/null +++ b/docs/development/git-workflow.md @@ -0,0 +1,433 @@ +# Git Workflow + +## Branch Strategy + +### Main Branches + +``` +main (production) + └── develop (staging) +``` + +- **main**: Production-ready code +- **develop**: Integration branch for features + +### Supporting Branches + +``` +develop + ├── feature/user-authentication + ├── feature/billing-system + ├── bugfix/payment-timeout + └── hotfix/security-patch +``` + +## Branch Naming Convention + +### Feature Branches +``` +feature/short-description +feature/user-auth +feature/payment-gateway +``` + +### Bugfix Branches +``` +bugfix/issue-description +bugfix/payment-timeout +bugfix/email-sending +``` + +### Hotfix Branches +``` +hotfix/critical-issue +hotfix/security-vuln +hotfix/data-loss +``` + +## Commit Message Format + +### Structure + +``` +(): + + + +