diff --git a/.copier/.copier-answers.yml.jinja b/.copier/.copier-answers.yml.jinja deleted file mode 100644 index 0028a2398a..0000000000 --- a/.copier/.copier-answers.yml.jinja +++ /dev/null @@ -1 +0,0 @@ -{{ _copier_answers|to_json -}} diff --git a/.copier/update_dotenv.py b/.copier/update_dotenv.py deleted file mode 100644 index 6576885626..0000000000 --- a/.copier/update_dotenv.py +++ /dev/null @@ -1,26 +0,0 @@ -from pathlib import Path -import json - -# Update the .env file with the answers from the .copier-answers.yml file -# without using Jinja2 templates in the .env file, this way the code works as is -# without needing Copier, but if Copier is used, the .env file will be updated -root_path = Path(__file__).parent.parent -answers_path = Path(__file__).parent / ".copier-answers.yml" -answers = json.loads(answers_path.read_text()) -env_path = root_path / ".env" -env_content = env_path.read_text() -lines = [] -for line in env_content.splitlines(): - for key, value in answers.items(): - upper_key = key.upper() - if line.startswith(f"{upper_key}="): - if " " in value: - content = f"{upper_key}={value!r}" - else: - content = f"{upper_key}={value}" - new_line = line.replace(line, content) - lines.append(new_line) - break - else: - lines.append(line) -env_path.write_text("\n".join(lines)) diff --git a/.env b/.env index 1d44286e25..e485be8379 100644 --- a/.env +++ b/.env @@ -13,14 +13,14 @@ FRONTEND_HOST=http://localhost:5173 # Environment: local, staging, production ENVIRONMENT=local -PROJECT_NAME="Full Stack FastAPI Project" -STACK_NAME=full-stack-fastapi-project +PROJECT_NAME=Service Data Platform +STACK_NAME=sdp-project # Backend -BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com" -SECRET_KEY=changethis +BACKEND_CORS_ORIGINS=["http://localhost:5173"] +SECRET_KEY=changeme123 FIRST_SUPERUSER=admin@example.com -FIRST_SUPERUSER_PASSWORD=changethis +FIRST_SUPERUSER_PASSWORD=changeme123 # Emails SMTP_HOST= @@ -31,15 +31,18 @@ SMTP_TLS=True SMTP_SSL=False SMTP_PORT=587 -# Postgres -POSTGRES_SERVER=localhost -POSTGRES_PORT=5432 -POSTGRES_DB=app -POSTGRES_USER=postgres -POSTGRES_PASSWORD=changethis +# Postgres (commented out as we're using SQLite) +# POSTGRES_SERVER=localhost +# POSTGRES_PORT=5432 +# POSTGRES_DB=app +# POSTGRES_USER=postgres +# POSTGRES_PASSWORD=changethis SENTRY_DSN= # Configure these with your own Docker registry images DOCKER_IMAGE_BACKEND=backend DOCKER_IMAGE_FRONTEND=frontend + +# SQLite configuration +SQLITE_DB=./sqlite.db diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index efdba87644..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -* text=auto -*.sh text eol=lf diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml deleted file mode 100644 index af6abf4661..0000000000 --- a/.github/DISCUSSION_TEMPLATE/questions.yml +++ /dev/null @@ -1,118 +0,0 @@ -labels: [question] -body: - - type: markdown - attributes: - value: | - Thanks for your interest in this project! πŸš€ - - Please follow these instructions, fill every question, and do every step. πŸ™ - - I'm asking this because answering questions and solving problems in GitHub is what consumes most of the time. - - I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling questions. - - All that, on top of all the incredible help provided by a bunch of community members, that give a lot of their time to come here and help others. - - That's a lot of work, but if more users came to help others like them just a little bit more, it would be much less effort for them (and you and me πŸ˜…). - - By asking questions in a structured way (following this) it will be much easier to help you. - - And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎 - - As there are too many questions, I'll have to discard and close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. πŸ€“ - - type: checkboxes - id: checks - attributes: - label: First Check - description: Please confirm and check all the following options. - options: - - label: I added a very descriptive title here. - required: true - - label: I used the GitHub search to find a similar question and didn't find it. - required: true - - label: I searched in the documentation/README. - required: true - - label: I already searched in Google "How to do X" and didn't find any information. - required: true - - label: I already read and followed all the tutorial in the docs/README and didn't find an answer. - required: true - - type: checkboxes - id: help - attributes: - label: Commit to Help - description: | - After submitting this, I commit to one of: - - * Read open questions until I find 2 where I can help someone and add a comment to help there. - * I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future. - - options: - - label: I commit to help with one of those options πŸ‘† - required: true - - type: textarea - id: example - attributes: - label: Example Code - description: | - Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case. - - If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you. - - placeholder: | - Write your example code here. - render: Text - validations: - required: true - - type: textarea - id: description - attributes: - label: Description - description: | - What is the problem, question, or error? - - Write a short description telling me what you are doing, what you expect to happen, and what is currently happening. - placeholder: | - * Open the browser and call the endpoint `/`. - * It returns a JSON with `{"message": "Hello World"}`. - * But I expected it to return `{"message": "Hello Morty"}`. - validations: - required: true - - type: dropdown - id: os - attributes: - label: Operating System - description: What operating system are you on? - multiple: true - options: - - Linux - - Windows - - macOS - - Other - validations: - required: true - - type: textarea - id: os-details - attributes: - label: Operating System Details - description: You can add more details about your operating system here, in particular if you chose "Other". - validations: - required: true - - type: input - id: python-version - attributes: - label: Python Version - description: | - What Python version are you using? - - You can find the Python version with: - - ```bash - python --version - ``` - validations: - required: true - - type: textarea - id: context - attributes: - label: Additional Context - description: Add any additional context information or screenshots you think are useful. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 0ffc101a3f..0000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: [tiangolo] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 50bde36072..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Security Contact - about: Please report security vulnerabilities to security@tiangolo.com - - name: Question or Problem - about: Ask a question or ask about a problem in GitHub Discussions. - url: https://github.com/fastapi/full-stack-fastapi-template/discussions/categories/questions - - name: Feature Request - about: To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already. - url: https://github.com/fastapi/full-stack-fastapi-template/discussions/categories/questions diff --git a/.github/ISSUE_TEMPLATE/privileged.yml b/.github/ISSUE_TEMPLATE/privileged.yml deleted file mode 100644 index 6438848c83..0000000000 --- a/.github/ISSUE_TEMPLATE/privileged.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Privileged -description: You are @tiangolo or he asked you directly to create an issue here. If not, check the other options. πŸ‘‡ -body: - - type: markdown - attributes: - value: | - Thanks for your interest in this project! πŸš€ - - If you are not @tiangolo or he didn't ask you directly to create an issue here, please start the conversation in a [Question in GitHub Discussions](https://github.com/tiangolo/full-stack-fastapi-template/discussions/categories/questions) instead. - - type: checkboxes - id: privileged - attributes: - label: Privileged issue - description: Confirm that you are allowed to create an issue here. - options: - - label: I'm @tiangolo or he asked me directly to create an issue here. - required: true - - type: textarea - id: content - attributes: - label: Issue Content - description: Add the content of the issue here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 10bd4a4edd..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: 2 -updates: - # GitHub Actions - - package-ecosystem: github-actions - directory: / - schedule: - interval: daily - commit-message: - prefix: ⬆ - # Python - - package-ecosystem: pip - directory: / - schedule: - interval: daily - commit-message: - prefix: ⬆ - # npm - - package-ecosystem: npm - directory: / - schedule: - interval: daily - commit-message: - prefix: ⬆ - # Docker - - package-ecosystem: docker - directory: / - schedule: - interval: weekly - commit-message: - prefix: ⬆ diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index ed657c23d7..0000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,25 +0,0 @@ -docs: - - all: - - changed-files: - - any-glob-to-any-file: - - '**/*.md' - - all-globs-to-all-files: - - '!frontend/**' - - '!backend/**' - - '!.github/**' - - '!scripts/**' - - '!.gitignore' - - '!.pre-commit-config.yaml' - -internal: - - all: - - changed-files: - - any-glob-to-any-file: - - .github/** - - scripts/** - - .gitignore - - .pre-commit-config.yaml - - all-globs-to-all-files: - - '!./**/*.md' - - '!frontend/**' - - '!backend/**' diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml deleted file mode 100644 index dccea83f35..0000000000 --- a/.github/workflows/add-to-project.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Add to Project - -on: - pull_request_target: - issues: - types: - - opened - - reopened - -jobs: - add-to-project: - name: Add to project - runs-on: ubuntu-latest - steps: - - uses: actions/add-to-project@v1.0.2 - with: - project-url: https://github.com/orgs/fastapi/projects/2 - github-token: ${{ secrets.PROJECTS_TOKEN }} diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml deleted file mode 100644 index a64d02a156..0000000000 --- a/.github/workflows/deploy-production.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Deploy to Production - -on: - release: - types: - - published - -jobs: - deploy: - # Do not deploy in the main repository, only in user projects - if: github.repository_owner != 'fastapi' - runs-on: - - self-hosted - - production - env: - ENVIRONMENT: production - DOMAIN: ${{ secrets.DOMAIN_PRODUCTION }} - STACK_NAME: ${{ secrets.STACK_NAME_PRODUCTION }} - SECRET_KEY: ${{ secrets.SECRET_KEY }} - FIRST_SUPERUSER: ${{ secrets.FIRST_SUPERUSER }} - FIRST_SUPERUSER_PASSWORD: ${{ secrets.FIRST_SUPERUSER_PASSWORD }} - SMTP_HOST: ${{ secrets.SMTP_HOST }} - SMTP_USER: ${{ secrets.SMTP_USER }} - SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} - EMAILS_FROM_EMAIL: ${{ secrets.EMAILS_FROM_EMAIL }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - run: docker compose -f docker-compose.yml --project-name ${{ secrets.STACK_NAME_PRODUCTION }} build - - run: docker compose -f docker-compose.yml --project-name ${{ secrets.STACK_NAME_PRODUCTION }} up -d diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml deleted file mode 100644 index 26bd692fd8..0000000000 --- a/.github/workflows/deploy-staging.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Deploy to Staging - -on: - push: - branches: - - master - -jobs: - deploy: - # Do not deploy in the main repository, only in user projects - if: github.repository_owner != 'fastapi' - runs-on: - - self-hosted - - staging - env: - ENVIRONMENT: staging - DOMAIN: ${{ secrets.DOMAIN_STAGING }} - STACK_NAME: ${{ secrets.STACK_NAME_STAGING }} - SECRET_KEY: ${{ secrets.SECRET_KEY }} - FIRST_SUPERUSER: ${{ secrets.FIRST_SUPERUSER }} - FIRST_SUPERUSER_PASSWORD: ${{ secrets.FIRST_SUPERUSER_PASSWORD }} - SMTP_HOST: ${{ secrets.SMTP_HOST }} - SMTP_USER: ${{ secrets.SMTP_USER }} - SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} - EMAILS_FROM_EMAIL: ${{ secrets.EMAILS_FROM_EMAIL }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - run: docker compose -f docker-compose.yml --project-name ${{ secrets.STACK_NAME_STAGING }} build - - run: docker compose -f docker-compose.yml --project-name ${{ secrets.STACK_NAME_STAGING }} up -d diff --git a/.github/workflows/generate-client.yml b/.github/workflows/generate-client.yml deleted file mode 100644 index 304363ce96..0000000000 --- a/.github/workflows/generate-client.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Generate Client - -on: - pull_request: - types: - - opened - - synchronize - -jobs: - generate-client: - permissions: - contents: write - runs-on: ubuntu-latest - steps: - # For PRs from forks - - uses: actions/checkout@v4 - # For PRs from the same repo - - uses: actions/checkout@v4 - if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' ) - with: - ref: ${{ github.head_ref }} - token: ${{ secrets.FULL_STACK_FASTAPI_TEMPLATE_REPO_TOKEN }} - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - version: "0.4.15" - enable-cache: true - - name: Install dependencies - run: npm ci - working-directory: frontend - - run: uv sync - working-directory: backend - - run: uv run bash scripts/generate-client.sh - env: - VIRTUAL_ENV: backend/.venv - ENVIRONMENT: production - SECRET_KEY: just-for-generating-client - POSTGRES_PASSWORD: just-for-generating-client - FIRST_SUPERUSER_PASSWORD: just-for-generating-client - - name: Add changes to git - run: | - git config --local user.email "github-actions@github.com" - git config --local user.name "github-actions" - git add frontend/src/client - # Same repo PRs - - name: Push changes - if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' ) - run: | - git diff --staged --quiet || git commit -m "✨ Autogenerate frontend client" - git push - # Fork PRs - - name: Check changes - if: ( github.event_name == 'pull_request' && github.secret_source != 'Actions' ) - run: | - git diff --staged --quiet || (echo "Changes detected in generated client, run scripts/generate-client.sh and commit the changes" && exit 1) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml deleted file mode 100644 index 109ac0e989..0000000000 --- a/.github/workflows/issue-manager.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Issue Manager - -on: - schedule: - - cron: "21 17 * * *" - issue_comment: - types: - - created - issues: - types: - - labeled - pull_request_target: - types: - - labeled - workflow_dispatch: - -permissions: - issues: write - pull-requests: write - -jobs: - issue-manager: - if: github.repository_owner == 'fastapi' - runs-on: ubuntu-latest - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - uses: tiangolo/issue-manager@0.5.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - config: > - { - "answered": { - "delay": 864000, - "message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs." - }, - "waiting": { - "delay": 2628000, - "message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." - }, - "invalid": { - "delay": 0, - "message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details." - } - } diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index e8e58015a2..0000000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Labels -on: - pull_request_target: - types: - - opened - - synchronize - - reopened - # For label-checker - - labeled - - unlabeled - -jobs: - labeler: - permissions: - contents: read - pull-requests: write - runs-on: ubuntu-latest - steps: - - uses: actions/labeler@v5 - if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }} - - run: echo "Done adding labels" - # Run this after labeler applied labels - check-labels: - needs: - - labeler - permissions: - pull-requests: read - runs-on: ubuntu-latest - steps: - - uses: docker://agilepathway/pull-request-label-checker:latest - with: - one_of: breaking,security,feature,bug,refactor,upgrade,docs,lang-all,internal - repo_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml deleted file mode 100644 index 607c5243b0..0000000000 --- a/.github/workflows/latest-changes.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Latest Changes - -on: - pull_request_target: - branches: - - master - types: - - closed - workflow_dispatch: - inputs: - number: - description: PR number - required: true - debug_enabled: - description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" - required: false - default: "false" - -jobs: - latest-changes: - runs-on: ubuntu-latest - permissions: - pull-requests: read - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - uses: actions/checkout@v4 - with: - # To allow latest-changes to commit to the main branch - token: ${{ secrets.LATEST_CHANGES }} - - uses: tiangolo/latest-changes@0.3.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - latest_changes_file: ./release-notes.md - latest_changes_header: "## Latest Changes" - end_regex: "^## " - debug_logs: true - label_header_prefix: "### " diff --git a/.github/workflows/lint-backend.yml b/.github/workflows/lint-backend.yml deleted file mode 100644 index a6e536bffe..0000000000 --- a/.github/workflows/lint-backend.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Lint Backend - -on: - push: - branches: - - master - pull_request: - types: - - opened - - synchronize - -jobs: - lint-backend: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - version: "0.4.15" - enable-cache: true - - run: uv run bash scripts/lint.sh - working-directory: backend diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index 5b13c58689..0000000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: Playwright Tests - -on: - push: - branches: - - master - pull_request: - types: - - opened - - synchronize - workflow_dispatch: - inputs: - debug_enabled: - description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' - required: false - default: 'false' - -jobs: - changes: - runs-on: ubuntu-latest - # Set job outputs to values from filter step - outputs: - changed: ${{ steps.filter.outputs.changed }} - steps: - - uses: actions/checkout@v4 - # For pull requests it's not necessary to checkout the code but for the main branch it is - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - changed: - - backend/** - - frontend/** - - .env - - docker-compose*.yml - - .github/workflows/playwright.yml - - test-playwright: - needs: - - changes - if: ${{ needs.changes.outputs.changed == 'true' }} - timeout-minutes: 60 - runs-on: ubuntu-latest - strategy: - matrix: - shardIndex: [1, 2, 3, 4] - shardTotal: [4] - fail-fast: false - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} - with: - limit-access-to-actor: true - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - version: "0.4.15" - enable-cache: true - - run: uv sync - working-directory: backend - - run: npm ci - working-directory: frontend - - run: uv run bash scripts/generate-client.sh - env: - VIRTUAL_ENV: backend/.venv - - run: docker compose build - - run: docker compose down -v --remove-orphans - - name: Run Playwright tests - run: docker compose run --rm playwright npx playwright test --fail-on-flaky-tests --trace=retain-on-failure --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} - - run: docker compose down -v --remove-orphans - - name: Upload blob report to GitHub Actions Artifacts - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v4 - with: - name: blob-report-${{ matrix.shardIndex }} - path: frontend/blob-report - include-hidden-files: true - retention-days: 1 - - merge-playwright-reports: - needs: - - test-playwright - - changes - # Merge reports after playwright-tests, even if some shards have failed - if: ${{ !cancelled() && needs.changes.outputs.changed == 'true' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Install dependencies - run: npm ci - working-directory: frontend - - name: Download blob reports from GitHub Actions Artifacts - uses: actions/download-artifact@v4 - with: - path: frontend/all-blob-reports - pattern: blob-report-* - merge-multiple: true - - name: Merge into HTML Report - run: npx playwright merge-reports --reporter html ./all-blob-reports - working-directory: frontend - - name: Upload HTML report - uses: actions/upload-artifact@v4 - with: - name: html-report--attempt-${{ github.run_attempt }} - path: frontend/playwright-report - retention-days: 30 - include-hidden-files: true - - # https://github.com/marketplace/actions/alls-green#why - alls-green-playwright: # This job does nothing and is only used for the branch protection - if: always() - needs: - - test-playwright - runs-on: ubuntu-latest - steps: - - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@release/v1 - with: - jobs: ${{ toJSON(needs) }} - allowed-skips: test-playwright diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml deleted file mode 100644 index 61fde520ea..0000000000 --- a/.github/workflows/smokeshow.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Smokeshow - -on: - workflow_run: - workflows: [Test Backend] - types: [completed] - -jobs: - smokeshow: - if: ${{ github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-latest - permissions: - actions: read - statuses: write - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - run: pip install smokeshow - - uses: actions/download-artifact@v4 - with: - name: coverage-html - path: backend/htmlcov - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - - run: smokeshow upload backend/htmlcov - env: - SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage} - SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 90 - SMOKESHOW_GITHUB_CONTEXT: coverage - SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} - SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }} diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml deleted file mode 100644 index cbbb78de46..0000000000 --- a/.github/workflows/test-backend.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Test Backend - -on: - push: - branches: - - master - pull_request: - types: - - opened - - synchronize - -jobs: - test-backend: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - version: "0.4.15" - enable-cache: true - - run: docker compose down -v --remove-orphans - - run: docker compose up -d db mailcatcher - - name: Migrate DB - run: uv run bash scripts/prestart.sh - working-directory: backend - - name: Run tests - run: uv run bash scripts/tests-start.sh "Coverage for ${{ github.sha }}" - working-directory: backend - - run: docker compose down -v --remove-orphans - - name: Store coverage files - uses: actions/upload-artifact@v4 - with: - name: coverage-html - path: backend/htmlcov - include-hidden-files: true diff --git a/.github/workflows/test-docker-compose.yml b/.github/workflows/test-docker-compose.yml deleted file mode 100644 index 17792ede50..0000000000 --- a/.github/workflows/test-docker-compose.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Test Docker Compose - -on: - push: - branches: - - master - pull_request: - types: - - opened - - synchronize - -jobs: - - test-docker-compose: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - run: docker compose build - - run: docker compose down -v --remove-orphans - - run: docker compose up -d --wait backend frontend adminer - - name: Test backend is up - run: curl http://localhost:8000/api/v1/utils/health-check - - name: Test frontend is up - run: curl http://localhost:5173 - - run: docker compose down -v --remove-orphans diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a6dd346572..0000000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.vscode -node_modules/ -/test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 512da0105c..0000000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: check-added-large-files - - id: check-toml - - id: check-yaml - args: - - --unsafe - - id: end-of-file-fixer - exclude: | - (?x)^( - frontend/src/client/.*| - backend/app/email-templates/build/.* - )$ - - id: trailing-whitespace - exclude: ^frontend/src/client/.* - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.2.2 - hooks: - - id: ruff - args: - - --fix - - id: ruff-format - -ci: - autofix_commit_msg: 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks - autoupdate_commit_msg: ⬆ [pre-commit.ci] pre-commit autoupdate diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f11987b50c..0000000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 SebastiΓ‘n RamΓ­rez - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index afe124f3fb..0000000000 --- a/README.md +++ /dev/null @@ -1,239 +0,0 @@ -# Full Stack FastAPI Template - -Test -Coverage - -## Technology Stack and Features - -- ⚑ [**FastAPI**](https://fastapi.tiangolo.com) for the Python backend API. - - 🧰 [SQLModel](https://sqlmodel.tiangolo.com) for the Python SQL database interactions (ORM). - - πŸ” [Pydantic](https://docs.pydantic.dev), used by FastAPI, for the data validation and settings management. - - πŸ’Ύ [PostgreSQL](https://www.postgresql.org) as the SQL database. -- πŸš€ [React](https://react.dev) for the frontend. - - πŸ’ƒ Using TypeScript, hooks, Vite, and other parts of a modern frontend stack. - - 🎨 [Chakra UI](https://chakra-ui.com) for the frontend components. - - πŸ€– An automatically generated frontend client. - - πŸ§ͺ [Playwright](https://playwright.dev) for End-to-End testing. - - πŸ¦‡ Dark mode support. -- πŸ‹ [Docker Compose](https://www.docker.com) for development and production. -- πŸ”’ Secure password hashing by default. -- πŸ”‘ JWT (JSON Web Token) authentication. -- πŸ“« Email based password recovery. -- βœ… Tests with [Pytest](https://pytest.org). -- πŸ“ž [Traefik](https://traefik.io) as a reverse proxy / load balancer. -- 🚒 Deployment instructions using Docker Compose, including how to set up a frontend Traefik proxy to handle automatic HTTPS certificates. -- 🏭 CI (continuous integration) and CD (continuous deployment) based on GitHub Actions. - -### Dashboard Login - -[![API docs](img/login.png)](https://github.com/fastapi/full-stack-fastapi-template) - -### Dashboard - Admin - -[![API docs](img/dashboard.png)](https://github.com/fastapi/full-stack-fastapi-template) - -### Dashboard - Create User - -[![API docs](img/dashboard-create.png)](https://github.com/fastapi/full-stack-fastapi-template) - -### Dashboard - Items - -[![API docs](img/dashboard-items.png)](https://github.com/fastapi/full-stack-fastapi-template) - -### Dashboard - User Settings - -[![API docs](img/dashboard-user-settings.png)](https://github.com/fastapi/full-stack-fastapi-template) - -### Dashboard - Dark Mode - -[![API docs](img/dashboard-dark.png)](https://github.com/fastapi/full-stack-fastapi-template) - -### Interactive API Documentation - -[![API docs](img/docs.png)](https://github.com/fastapi/full-stack-fastapi-template) - -## How To Use It - -You can **just fork or clone** this repository and use it as is. - -✨ It just works. ✨ - -### How to Use a Private Repository - -If you want to have a private repository, GitHub won't allow you to simply fork it as it doesn't allow changing the visibility of forks. - -But you can do the following: - -- Create a new GitHub repo, for example `my-full-stack`. -- Clone this repository manually, set the name with the name of the project you want to use, for example `my-full-stack`: - -```bash -git clone git@github.com:fastapi/full-stack-fastapi-template.git my-full-stack -``` - -- Enter into the new directory: - -```bash -cd my-full-stack -``` - -- Set the new origin to your new repository, copy it from the GitHub interface, for example: - -```bash -git remote set-url origin git@github.com:octocat/my-full-stack.git -``` - -- Add this repo as another "remote" to allow you to get updates later: - -```bash -git remote add upstream git@github.com:fastapi/full-stack-fastapi-template.git -``` - -- Push the code to your new repository: - -```bash -git push -u origin master -``` - -### Update From the Original Template - -After cloning the repository, and after doing changes, you might want to get the latest changes from this original template. - -- Make sure you added the original repository as a remote, you can check it with: - -```bash -git remote -v - -origin git@github.com:octocat/my-full-stack.git (fetch) -origin git@github.com:octocat/my-full-stack.git (push) -upstream git@github.com:fastapi/full-stack-fastapi-template.git (fetch) -upstream git@github.com:fastapi/full-stack-fastapi-template.git (push) -``` - -- Pull the latest changes without merging: - -```bash -git pull --no-commit upstream master -``` - -This will download the latest changes from this template without committing them, that way you can check everything is right before committing. - -- If there are conflicts, solve them in your editor. - -- Once you are done, commit the changes: - -```bash -git merge --continue -``` - -### Configure - -You can then update configs in the `.env` files to customize your configurations. - -Before deploying it, make sure you change at least the values for: - -- `SECRET_KEY` -- `FIRST_SUPERUSER_PASSWORD` -- `POSTGRES_PASSWORD` - -You can (and should) pass these as environment variables from secrets. - -Read the [deployment.md](./deployment.md) docs for more details. - -### Generate Secret Keys - -Some environment variables in the `.env` file have a default value of `changethis`. - -You have to change them with a secret key, to generate secret keys you can run the following command: - -```bash -python -c "import secrets; print(secrets.token_urlsafe(32))" -``` - -Copy the content and use that as password / secret key. And run that again to generate another secure key. - -## How To Use It - Alternative With Copier - -This repository also supports generating a new project using [Copier](https://copier.readthedocs.io). - -It will copy all the files, ask you configuration questions, and update the `.env` files with your answers. - -### Install Copier - -You can install Copier with: - -```bash -pip install copier -``` - -Or better, if you have [`pipx`](https://pipx.pypa.io/), you can run it with: - -```bash -pipx install copier -``` - -**Note**: If you have `pipx`, installing copier is optional, you could run it directly. - -### Generate a Project With Copier - -Decide a name for your new project's directory, you will use it below. For example, `my-awesome-project`. - -Go to the directory that will be the parent of your project, and run the command with your project's name: - -```bash -copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trust -``` - -If you have `pipx` and you didn't install `copier`, you can run it directly: - -```bash -pipx run copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trust -``` - -**Note** the `--trust` option is necessary to be able to execute a [post-creation script](https://github.com/fastapi/full-stack-fastapi-template/blob/master/.copier/update_dotenv.py) that updates your `.env` files. - -### Input Variables - -Copier will ask you for some data, you might want to have at hand before generating the project. - -But don't worry, you can just update any of that in the `.env` files afterwards. - -The input variables, with their default values (some auto generated) are: - -- `project_name`: (default: `"FastAPI Project"`) The name of the project, shown to API users (in .env). -- `stack_name`: (default: `"fastapi-project"`) The name of the stack used for Docker Compose labels and project name (no spaces, no periods) (in .env). -- `secret_key`: (default: `"changethis"`) The secret key for the project, used for security, stored in .env, you can generate one with the method above. -- `first_superuser`: (default: `"admin@example.com"`) The email of the first superuser (in .env). -- `first_superuser_password`: (default: `"changethis"`) The password of the first superuser (in .env). -- `smtp_host`: (default: "") The SMTP server host to send emails, you can set it later in .env. -- `smtp_user`: (default: "") The SMTP server user to send emails, you can set it later in .env. -- `smtp_password`: (default: "") The SMTP server password to send emails, you can set it later in .env. -- `emails_from_email`: (default: `"info@example.com"`) The email account to send emails from, you can set it later in .env. -- `postgres_password`: (default: `"changethis"`) The password for the PostgreSQL database, stored in .env, you can generate one with the method above. -- `sentry_dsn`: (default: "") The DSN for Sentry, if you are using it, you can set it later in .env. - -## Backend Development - -Backend docs: [backend/README.md](./backend/README.md). - -## Frontend Development - -Frontend docs: [frontend/README.md](./frontend/README.md). - -## Deployment - -Deployment docs: [deployment.md](./deployment.md). - -## Development - -General development docs: [development.md](./development.md). - -This includes using Docker Compose, custom local domains, `.env` configurations, etc. - -## Release Notes - -Check the file [release-notes.md](./release-notes.md). - -## License - -The Full Stack FastAPI Template is licensed under the terms of the MIT license. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 0045fb8182..0000000000 --- a/SECURITY.md +++ /dev/null @@ -1,29 +0,0 @@ -# Security Policy - -Security is very important for this project and its community. πŸ”’ - -Learn more about it below. πŸ‘‡ - -## Versions - -The latest version or release is supported. - -You are encouraged to write tests for your application and update your versions frequently after ensuring that your tests are passing. This way you will benefit from the latest features, bug fixes, and **security fixes**. - -## Reporting a Vulnerability - -If you think you found a vulnerability, and even if you are not sure about it, please report it right away by sending an email to: security@tiangolo.com. Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue. - -I (the author, [@tiangolo](https://twitter.com/tiangolo)) will review it thoroughly and get back to you. - -## Public Discussions - -Please restrain from publicly discussing a potential security vulnerability. πŸ™Š - -It's better to discuss privately and try to find a solution first, to limit the potential impact as much as possible. - ---- - -Thanks for your help! - -The community and I thank you for that. πŸ™‡ diff --git a/backend/Dockerfile b/backend/Dockerfile index 44c53f0365..d800cf6df9 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,43 +1,37 @@ -FROM python:3.10 +FROM python:3.11-slim ENV PYTHONUNBUFFERED=1 WORKDIR /app/ -# Install uv -# Ref: https://docs.astral.sh/uv/guides/integration/docker/#installing-uv -COPY --from=ghcr.io/astral-sh/uv:0.5.11 /uv /uvx /bin/ +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + && rm -rf /var/lib/apt/lists/* -# Place executables in the environment at the front of the path -# Ref: https://docs.astral.sh/uv/guides/integration/docker/#using-the-environment -ENV PATH="/app/.venv/bin:$PATH" +# Install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt -# Compile bytecode -# Ref: https://docs.astral.sh/uv/guides/integration/docker/#compiling-bytecode -ENV UV_COMPILE_BYTECODE=1 +# Copy application code +COPY ./app /app/app -# uv Cache -# Ref: https://docs.astral.sh/uv/guides/integration/docker/#caching -ENV UV_LINK_MODE=copy +# Create a non-root user +RUN useradd -m -u 1000 appuser && \ + chown -R appuser:appuser /app -# Install dependencies -# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers -RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --frozen --no-install-project +# Switch to non-root user +USER appuser +# Set environment variables ENV PYTHONPATH=/app +ENV ENVIRONMENT=production -COPY ./scripts /app/scripts - -COPY ./pyproject.toml ./uv.lock ./alembic.ini /app/ - -COPY ./app /app/app +# Expose port +EXPOSE 8000 -# Sync the project -# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync +# Init database +RUN python3.11 ./app/initial_data.py -CMD ["fastapi", "run", "--workers", "4", "app/main.py"] +# Start the application +CMD ["python3.11", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--log-level", "debug"] \ No newline at end of file diff --git a/backend/alembic.ini b/backend/alembic.ini deleted file mode 100755 index 24841c2bfb..0000000000 --- a/backend/alembic.ini +++ /dev/null @@ -1,71 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts -script_location = app/alembic - -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# timezone to use when rendering the date -# within the migration file as well as the filename. -# 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 alembic/versions. When using multiple version -# directories, initial revisions must be specified with --version-path -# version_locations = %(here)s/bar %(here)s/bat alembic/versions - -# the output encoding used when revision files -# are written from script.py.mako -# output_encoding = utf-8 - -# 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/backend/app/alembic/README b/backend/app/alembic/README deleted file mode 100755 index 2500aa1bcf..0000000000 --- a/backend/app/alembic/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. diff --git a/backend/app/alembic/env.py b/backend/app/alembic/env.py deleted file mode 100755 index 7f29c04680..0000000000 --- a/backend/app/alembic/env.py +++ /dev/null @@ -1,84 +0,0 @@ -import os -from logging.config import fileConfig - -from alembic import context -from sqlalchemy import engine_from_config, pool - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -# target_metadata = None - -from app.models import SQLModel # noqa -from app.core.config import settings # noqa - -target_metadata = SQLModel.metadata - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def get_url(): - return str(settings.SQLALCHEMY_DATABASE_URI) - - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = get_url() - context.configure( - url=url, target_metadata=target_metadata, literal_binds=True, compare_type=True - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - configuration = config.get_section(config.config_ini_section) - configuration["sqlalchemy.url"] = get_url() - connectable = engine_from_config( - configuration, - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: - context.configure( - connection=connection, target_metadata=target_metadata, compare_type=True - ) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/backend/app/alembic/script.py.mako b/backend/app/alembic/script.py.mako deleted file mode 100755 index 217a9a8b7b..0000000000 --- a/backend/app/alembic/script.py.mako +++ /dev/null @@ -1,25 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -import sqlmodel.sql.sqltypes -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/backend/app/alembic/versions/.keep b/backend/app/alembic/versions/.keep deleted file mode 100755 index e69de29bb2..0000000000 diff --git a/backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py b/backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py deleted file mode 100644 index 10e47a1456..0000000000 --- a/backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Add cascade delete relationships - -Revision ID: 1a31ce608336 -Revises: d98dd8ec85a3 -Create Date: 2024-07-31 22:24:34.447891 - -""" -from alembic import op -import sqlalchemy as sa -import sqlmodel.sql.sqltypes - - -# revision identifiers, used by Alembic. -revision = '1a31ce608336' -down_revision = 'd98dd8ec85a3' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('item', 'owner_id', - existing_type=sa.UUID(), - nullable=False) - op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey') - op.create_foreign_key(None, 'item', 'user', ['owner_id'], ['id'], ondelete='CASCADE') - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'item', type_='foreignkey') - op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id']) - op.alter_column('item', 'owner_id', - existing_type=sa.UUID(), - nullable=True) - # ### end Alembic commands ### diff --git a/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py b/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py deleted file mode 100755 index 78a41773b9..0000000000 --- a/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Add max length for string(varchar) fields in User and Items models - -Revision ID: 9c0a54914c78 -Revises: e2412789c190 -Create Date: 2024-06-17 14:42:44.639457 - -""" -from alembic import op -import sqlalchemy as sa -import sqlmodel.sql.sqltypes - - -# revision identifiers, used by Alembic. -revision = '9c0a54914c78' -down_revision = 'e2412789c190' -branch_labels = None -depends_on = None - - -def upgrade(): - # Adjust the length of the email field in the User table - op.alter_column('user', 'email', - existing_type=sa.String(), - type_=sa.String(length=255), - existing_nullable=False) - - # Adjust the length of the full_name field in the User table - op.alter_column('user', 'full_name', - existing_type=sa.String(), - type_=sa.String(length=255), - existing_nullable=True) - - # Adjust the length of the title field in the Item table - op.alter_column('item', 'title', - existing_type=sa.String(), - type_=sa.String(length=255), - existing_nullable=False) - - # Adjust the length of the description field in the Item table - op.alter_column('item', 'description', - existing_type=sa.String(), - type_=sa.String(length=255), - existing_nullable=True) - - -def downgrade(): - # Revert the length of the email field in the User table - op.alter_column('user', 'email', - existing_type=sa.String(length=255), - type_=sa.String(), - existing_nullable=False) - - # Revert the length of the full_name field in the User table - op.alter_column('user', 'full_name', - existing_type=sa.String(length=255), - type_=sa.String(), - existing_nullable=True) - - # Revert the length of the title field in the Item table - op.alter_column('item', 'title', - existing_type=sa.String(length=255), - type_=sa.String(), - existing_nullable=False) - - # Revert the length of the description field in the Item table - op.alter_column('item', 'description', - existing_type=sa.String(length=255), - type_=sa.String(), - existing_nullable=True) diff --git a/backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py b/backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py deleted file mode 100755 index 37af1fa215..0000000000 --- a/backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Edit replace id integers in all models to use UUID instead - -Revision ID: d98dd8ec85a3 -Revises: 9c0a54914c78 -Create Date: 2024-07-19 04:08:04.000976 - -""" -from alembic import op -import sqlalchemy as sa -import sqlmodel.sql.sqltypes -from sqlalchemy.dialects import postgresql - - -# revision identifiers, used by Alembic. -revision = 'd98dd8ec85a3' -down_revision = '9c0a54914c78' -branch_labels = None -depends_on = None - - -def upgrade(): - # Ensure uuid-ossp extension is available - op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"') - - # Create a new UUID column with a default UUID value - op.add_column('user', sa.Column('new_id', postgresql.UUID(as_uuid=True), default=sa.text('uuid_generate_v4()'))) - op.add_column('item', sa.Column('new_id', postgresql.UUID(as_uuid=True), default=sa.text('uuid_generate_v4()'))) - op.add_column('item', sa.Column('new_owner_id', postgresql.UUID(as_uuid=True), nullable=True)) - - # Populate the new columns with UUIDs - op.execute('UPDATE "user" SET new_id = uuid_generate_v4()') - op.execute('UPDATE item SET new_id = uuid_generate_v4()') - op.execute('UPDATE item SET new_owner_id = (SELECT new_id FROM "user" WHERE "user".id = item.owner_id)') - - # Set the new_id as not nullable - op.alter_column('user', 'new_id', nullable=False) - op.alter_column('item', 'new_id', nullable=False) - - # Drop old columns and rename new columns - op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey') - op.drop_column('item', 'owner_id') - op.alter_column('item', 'new_owner_id', new_column_name='owner_id') - - op.drop_column('user', 'id') - op.alter_column('user', 'new_id', new_column_name='id') - - op.drop_column('item', 'id') - op.alter_column('item', 'new_id', new_column_name='id') - - # Create primary key constraint - op.create_primary_key('user_pkey', 'user', ['id']) - op.create_primary_key('item_pkey', 'item', ['id']) - - # Recreate foreign key constraint - op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id']) - -def downgrade(): - # Reverse the upgrade process - op.add_column('user', sa.Column('old_id', sa.Integer, autoincrement=True)) - op.add_column('item', sa.Column('old_id', sa.Integer, autoincrement=True)) - op.add_column('item', sa.Column('old_owner_id', sa.Integer, nullable=True)) - - # Populate the old columns with default values - # Generate sequences for the integer IDs if not exist - op.execute('CREATE SEQUENCE IF NOT EXISTS user_id_seq AS INTEGER OWNED BY "user".old_id') - op.execute('CREATE SEQUENCE IF NOT EXISTS item_id_seq AS INTEGER OWNED BY item.old_id') - - op.execute('SELECT setval(\'user_id_seq\', COALESCE((SELECT MAX(old_id) + 1 FROM "user"), 1), false)') - op.execute('SELECT setval(\'item_id_seq\', COALESCE((SELECT MAX(old_id) + 1 FROM item), 1), false)') - - op.execute('UPDATE "user" SET old_id = nextval(\'user_id_seq\')') - op.execute('UPDATE item SET old_id = nextval(\'item_id_seq\'), old_owner_id = (SELECT old_id FROM "user" WHERE "user".id = item.owner_id)') - - # Drop new columns and rename old columns back - op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey') - op.drop_column('item', 'owner_id') - op.alter_column('item', 'old_owner_id', new_column_name='owner_id') - - op.drop_column('user', 'id') - op.alter_column('user', 'old_id', new_column_name='id') - - op.drop_column('item', 'id') - op.alter_column('item', 'old_id', new_column_name='id') - - # Create primary key constraint - op.create_primary_key('user_pkey', 'user', ['id']) - op.create_primary_key('item_pkey', 'item', ['id']) - - # Recreate foreign key constraint - op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id']) diff --git a/backend/app/alembic/versions/e2412789c190_initialize_models.py b/backend/app/alembic/versions/e2412789c190_initialize_models.py deleted file mode 100644 index 7529ea91fa..0000000000 --- a/backend/app/alembic/versions/e2412789c190_initialize_models.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Initialize models - -Revision ID: e2412789c190 -Revises: -Create Date: 2023-11-24 22:55:43.195942 - -""" -import sqlalchemy as sa -import sqlmodel.sql.sqltypes -from alembic import op - -# revision identifiers, used by Alembic. -revision = "e2412789c190" -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "user", - sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("is_superuser", sa.Boolean(), nullable=False), - sa.Column("full_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column("id", sa.Integer(), nullable=False), - sa.Column( - "hashed_password", sqlmodel.sql.sqltypes.AutoString(), nullable=False - ), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) - op.create_table( - "item", - sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("title", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("owner_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["owner_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table("item") - op.drop_index(op.f("ix_user_email"), table_name="user") - op.drop_table("user") - # ### end Alembic commands ### diff --git a/backend/app/core/config.py b/backend/app/core/config.py index d58e03c87d..6b025ac962 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -7,11 +7,9 @@ BeforeValidator, EmailStr, HttpUrl, - PostgresDsn, computed_field, model_validator, ) -from pydantic_core import MultiHostUrl from pydantic_settings import BaseSettings, SettingsConfigDict from typing_extensions import Self @@ -49,25 +47,14 @@ def all_cors_origins(self) -> list[str]: self.FRONTEND_HOST ] - PROJECT_NAME: str + PROJECT_NAME: str = "Service Data Platform" SENTRY_DSN: HttpUrl | None = None - POSTGRES_SERVER: str - POSTGRES_PORT: int = 5432 - POSTGRES_USER: str - POSTGRES_PASSWORD: str = "" - POSTGRES_DB: str = "" + SQLITE_DB: str = "./sqlite.db" @computed_field # type: ignore[prop-decorator] @property - def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn: - return MultiHostUrl.build( - scheme="postgresql+psycopg", - username=self.POSTGRES_USER, - password=self.POSTGRES_PASSWORD, - host=self.POSTGRES_SERVER, - port=self.POSTGRES_PORT, - path=self.POSTGRES_DB, - ) + def SQLALCHEMY_DATABASE_URI(self) -> str: + return f"sqlite:///{self.SQLITE_DB}" SMTP_TLS: bool = True SMTP_SSL: bool = False @@ -92,8 +79,8 @@ def emails_enabled(self) -> bool: return bool(self.SMTP_HOST and self.EMAILS_FROM_EMAIL) EMAIL_TEST_USER: EmailStr = "test@example.com" - FIRST_SUPERUSER: EmailStr - FIRST_SUPERUSER_PASSWORD: str + FIRST_SUPERUSER: EmailStr = "admin@example.com" + FIRST_SUPERUSER_PASSWORD: str = "changeme123" def _check_default_secret(self, var_name: str, value: str | None) -> None: if value == "changethis": @@ -109,7 +96,6 @@ def _check_default_secret(self, var_name: str, value: str | None) -> None: @model_validator(mode="after") def _enforce_non_default_secrets(self) -> Self: self._check_default_secret("SECRET_KEY", self.SECRET_KEY) - self._check_default_secret("POSTGRES_PASSWORD", self.POSTGRES_PASSWORD) self._check_default_secret( "FIRST_SUPERUSER_PASSWORD", self.FIRST_SUPERUSER_PASSWORD ) @@ -117,4 +103,4 @@ def _enforce_non_default_secrets(self) -> Self: return self -settings = Settings() # type: ignore +settings = Settings() # type: ignore \ No newline at end of file diff --git a/backend/app/core/db.py b/backend/app/core/db.py index ba991fb36d..b89fcd25fe 100644 --- a/backend/app/core/db.py +++ b/backend/app/core/db.py @@ -1,33 +1,45 @@ -from sqlmodel import Session, create_engine, select +from collections.abc import Generator + +from sqlmodel import Session, SQLModel, create_engine from app import crud from app.core.config import settings -from app.models import User, UserCreate +from app.core.security import get_password_hash +from app.models import User -engine = create_engine(str(settings.SQLALCHEMY_DATABASE_URI)) +engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, + connect_args={"check_same_thread": False}, # Needed for SQLite + echo=True, # Log SQL queries +) # make sure all SQLModel models are imported (app.models) before initializing DB # otherwise, SQLModel might fail to initialize relationships properly # for more details: https://github.com/fastapi/full-stack-fastapi-template/issues/28 -def init_db(session: Session) -> None: - # Tables should be created with Alembic migrations - # But if you don't want to use migrations, create - # the tables un-commenting the next lines - # from sqlmodel import SQLModel +def get_session() -> Generator[Session, None, None]: + with Session(engine) as session: + yield session + - # This works because the models are already imported and registered from app.models - # SQLModel.metadata.create_all(engine) +def init_db(session: Session) -> None: + """Initialize the database by creating all tables and adding a superuser if it doesn't exist.""" + # Create all tables + SQLModel.metadata.create_all(engine) - user = session.exec( - select(User).where(User.email == settings.FIRST_SUPERUSER) - ).first() + # Create initial superuser if it doesn't exist + user = crud.get_user_by_email(session=session, email=settings.FIRST_SUPERUSER) if not user: - user_in = UserCreate( - email=settings.FIRST_SUPERUSER, - password=settings.FIRST_SUPERUSER_PASSWORD, - is_superuser=True, - ) - user = crud.create_user(session=session, user_create=user_in) + user_in = { + "email": settings.FIRST_SUPERUSER, + "hashed_password": get_password_hash( + settings.FIRST_SUPERUSER_PASSWORD + ), # Key change here + "is_superuser": True, + } + user = User(**user_in) + session.add(user) + session.commit() + + diff --git a/backend/pyproject.toml b/backend/pyproject.toml deleted file mode 100644 index 1c77b83ded..0000000000 --- a/backend/pyproject.toml +++ /dev/null @@ -1,68 +0,0 @@ -[project] -name = "app" -version = "0.1.0" -description = "" -requires-python = ">=3.10,<4.0" -dependencies = [ - "fastapi[standard]<1.0.0,>=0.114.2", - "python-multipart<1.0.0,>=0.0.7", - "email-validator<3.0.0.0,>=2.1.0.post1", - "passlib[bcrypt]<2.0.0,>=1.7.4", - "tenacity<9.0.0,>=8.2.3", - "pydantic>2.0", - "emails<1.0,>=0.6", - "jinja2<4.0.0,>=3.1.4", - "alembic<2.0.0,>=1.12.1", - "httpx<1.0.0,>=0.25.1", - "psycopg[binary]<4.0.0,>=3.1.13", - "sqlmodel<1.0.0,>=0.0.21", - # Pin bcrypt until passlib supports the latest - "bcrypt==4.0.1", - "pydantic-settings<3.0.0,>=2.2.1", - "sentry-sdk[fastapi]<2.0.0,>=1.40.6", - "pyjwt<3.0.0,>=2.8.0", -] - -[tool.uv] -dev-dependencies = [ - "pytest<8.0.0,>=7.4.3", - "mypy<2.0.0,>=1.8.0", - "ruff<1.0.0,>=0.2.2", - "pre-commit<4.0.0,>=3.6.2", - "types-passlib<2.0.0.0,>=1.7.7.20240106", - "coverage<8.0.0,>=7.4.3", -] - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.mypy] -strict = true -exclude = ["venv", ".venv", "alembic"] - -[tool.ruff] -target-version = "py310" -exclude = ["alembic"] - -[tool.ruff.lint] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "UP", # pyupgrade - "ARG001", # unused arguments in functions -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "W191", # indentation contains tabs - "B904", # Allow raising exceptions without from e, for HTTPException -] - -[tool.ruff.lint.pyupgrade] -# Preserve types, even if a file imports `from __future__ import annotations`. -keep-runtime-typing = true diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000000..aee0bf7e44 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,15 @@ +fastapi[standard]>=0.114.2,<1.0.0 +python-multipart>=0.0.7,<1.0.0 +email-validator>=2.1.0.post1,<3.0.0.0 +passlib[bcrypt]>=1.7.4,<2.0.0 +tenacity>=8.2.3,<9.0.0 +pydantic>2.0 +emails>=0.6,<1.0 +jinja2>=3.1.4,<4.0.0 +httpx>=0.25.1,<1.0.0 +sqlmodel>=0.0.21,<1.0.0 +bcrypt==4.0.1 +pydantic-settings>=2.2.1,<3.0.0 +sentry-sdk[fastapi]>=1.40.6,<2.0.0 +PyJWT>=2.8.0,<3.0.0 +python-dotenv>=1.0.0 \ No newline at end of file diff --git a/backend/scripts/prestart.sh b/backend/scripts/prestart.sh index 1b395d513f..54b9cb904f 100644 --- a/backend/scripts/prestart.sh +++ b/backend/scripts/prestart.sh @@ -3,11 +3,8 @@ set -e set -x -# Let the DB start -python app/backend_pre_start.py +export PYTHONPATH="${PYTHONPATH}:$(pwd)" -# Run migrations -alembic upgrade head +python app/tests_pre_start.py -# Create initial data in DB -python app/initial_data.py +bash scripts/test.sh "$@" diff --git a/backend/sqlite.db b/backend/sqlite.db new file mode 100644 index 0000000000..510aecdbb3 Binary files /dev/null and b/backend/sqlite.db differ diff --git a/copier.yml b/copier.yml deleted file mode 100644 index f98e3fc861..0000000000 --- a/copier.yml +++ /dev/null @@ -1,100 +0,0 @@ -project_name: - type: str - help: The name of the project, shown to API users (in .env) - default: FastAPI Project - -stack_name: - type: str - help: The name of the stack used for Docker Compose labels (no spaces) (in .env) - default: fastapi-project - -secret_key: - type: str - help: | - 'The secret key for the project, used for security, - stored in .env, you can generate one with: - python -c "import secrets; print(secrets.token_urlsafe(32))"' - default: changethis - -first_superuser: - type: str - help: The email of the first superuser (in .env) - default: admin@example.com - -first_superuser_password: - type: str - help: The password of the first superuser (in .env) - default: changethis - -smtp_host: - type: str - help: The SMTP server host to send emails, you can set it later in .env - default: "" - -smtp_user: - type: str - help: The SMTP server user to send emails, you can set it later in .env - default: "" - -smtp_password: - type: str - help: The SMTP server password to send emails, you can set it later in .env - default: "" - -emails_from_email: - type: str - help: The email account to send emails from, you can set it later in .env - default: info@example.com - -postgres_password: - type: str - help: | - 'The password for the PostgreSQL database, stored in .env, - you can generate one with: - python -c "import secrets; print(secrets.token_urlsafe(32))"' - default: changethis - -sentry_dsn: - type: str - help: The DSN for Sentry, if you are using it, you can set it later in .env - default: "" - -_exclude: - # Global - - .vscode - - .mypy_cache - # Python - - __pycache__ - - app.egg-info - - "*.pyc" - - .mypy_cache - - .coverage - - htmlcov - - .cache - - .venv - # Frontend - # Logs - - logs - - "*.log" - - npm-debug.log* - - yarn-debug.log* - - yarn-error.log* - - pnpm-debug.log* - - lerna-debug.log* - - node_modules - - dist - - dist-ssr - - "*.local" - # Editor directories and files - - .idea - - .DS_Store - - "*.suo" - - "*.ntvs*" - - "*.njsproj" - - "*.sln" - - "*.sw?" - -_answers_file: .copier/.copier-answers.yml - -_tasks: - - ["{{ _copier_python }}", .copier/update_dotenv.py] diff --git a/deployment.md b/deployment.md deleted file mode 100644 index eadf76ddae..0000000000 --- a/deployment.md +++ /dev/null @@ -1,309 +0,0 @@ -# FastAPI Project - Deployment - -You can deploy the project using Docker Compose to a remote server. - -This project expects you to have a Traefik proxy handling communication to the outside world and HTTPS certificates. - -You can use CI/CD (continuous integration and continuous deployment) systems to deploy automatically, there are already configurations to do it with GitHub Actions. - -But you have to configure a couple things first. πŸ€“ - -## Preparation - -* Have a remote server ready and available. -* Configure the DNS records of your domain to point to the IP of the server you just created. -* Configure a wildcard subdomain for your domain, so that you can have multiple subdomains for different services, e.g. `*.fastapi-project.example.com`. This will be useful for accessing different components, like `dashboard.fastapi-project.example.com`, `api.fastapi-project.example.com`, `traefik.fastapi-project.example.com`, `adminer.fastapi-project.example.com`, etc. And also for `staging`, like `dashboard.staging.fastapi-project.example.com`, `adminer.staging..fastapi-project.example.com`, etc. -* Install and configure [Docker](https://docs.docker.com/engine/install/) on the remote server (Docker Engine, not Docker Desktop). - -## Public Traefik - -We need a Traefik proxy to handle incoming connections and HTTPS certificates. - -You need to do these next steps only once. - -### Traefik Docker Compose - -* Create a remote directory to store your Traefik Docker Compose file: - -```bash -mkdir -p /root/code/traefik-public/ -``` - -Copy the Traefik Docker Compose file to your server. You could do it by running the command `rsync` in your local terminal: - -```bash -rsync -a docker-compose.traefik.yml root@your-server.example.com:/root/code/traefik-public/ -``` - -### Traefik Public Network - -This Traefik will expect a Docker "public network" named `traefik-public` to communicate with your stack(s). - -This way, there will be a single public Traefik proxy that handles the communication (HTTP and HTTPS) with the outside world, and then behind that, you could have one or more stacks with different domains, even if they are on the same single server. - -To create a Docker "public network" named `traefik-public` run the following command in your remote server: - -```bash -docker network create traefik-public -``` - -### Traefik Environment Variables - -The Traefik Docker Compose file expects some environment variables to be set in your terminal before starting it. You can do it by running the following commands in your remote server. - -* Create the username for HTTP Basic Auth, e.g.: - -```bash -export USERNAME=admin -``` - -* Create an environment variable with the password for HTTP Basic Auth, e.g.: - -```bash -export PASSWORD=changethis -``` - -* Use openssl to generate the "hashed" version of the password for HTTP Basic Auth and store it in an environment variable: - -```bash -export HASHED_PASSWORD=$(openssl passwd -apr1 $PASSWORD) -``` - -To verify that the hashed password is correct, you can print it: - -```bash -echo $HASHED_PASSWORD -``` - -* Create an environment variable with the domain name for your server, e.g.: - -```bash -export DOMAIN=fastapi-project.example.com -``` - -* Create an environment variable with the email for Let's Encrypt, e.g.: - -```bash -export EMAIL=admin@example.com -``` - -**Note**: you need to set a different email, an email `@example.com` won't work. - -### Start the Traefik Docker Compose - -Go to the directory where you copied the Traefik Docker Compose file in your remote server: - -```bash -cd /root/code/traefik-public/ -``` - -Now with the environment variables set and the `docker-compose.traefik.yml` in place, you can start the Traefik Docker Compose running the following command: - -```bash -docker compose -f docker-compose.traefik.yml up -d -``` - -## Deploy the FastAPI Project - -Now that you have Traefik in place you can deploy your FastAPI project with Docker Compose. - -**Note**: You might want to jump ahead to the section about Continuous Deployment with GitHub Actions. - -## Environment Variables - -You need to set some environment variables first. - -Set the `ENVIRONMENT`, by default `local` (for development), but when deploying to a server you would put something like `staging` or `production`: - -```bash -export ENVIRONMENT=production -``` - -Set the `DOMAIN`, by default `localhost` (for development), but when deploying you would use your own domain, for example: - -```bash -export DOMAIN=fastapi-project.example.com -``` - -You can set several variables, like: - -* `PROJECT_NAME`: The name of the project, used in the API for the docs and emails. -* `STACK_NAME`: The name of the stack used for Docker Compose labels and project name, this should be different for `staging`, `production`, etc. You could use the same domain replacing dots with dashes, e.g. `fastapi-project-example-com` and `staging-fastapi-project-example-com`. -* `BACKEND_CORS_ORIGINS`: A list of allowed CORS origins separated by commas. -* `SECRET_KEY`: The secret key for the FastAPI project, used to sign tokens. -* `FIRST_SUPERUSER`: The email of the first superuser, this superuser will be the one that can create new users. -* `FIRST_SUPERUSER_PASSWORD`: The password of the first superuser. -* `SMTP_HOST`: The SMTP server host to send emails, this would come from your email provider (E.g. Mailgun, Sparkpost, Sendgrid, etc). -* `SMTP_USER`: The SMTP server user to send emails. -* `SMTP_PASSWORD`: The SMTP server password to send emails. -* `EMAILS_FROM_EMAIL`: The email account to send emails from. -* `POSTGRES_SERVER`: The hostname of the PostgreSQL server. You can leave the default of `db`, provided by the same Docker Compose. You normally wouldn't need to change this unless you are using a third-party provider. -* `POSTGRES_PORT`: The port of the PostgreSQL server. You can leave the default. You normally wouldn't need to change this unless you are using a third-party provider. -* `POSTGRES_PASSWORD`: The Postgres password. -* `POSTGRES_USER`: The Postgres user, you can leave the default. -* `POSTGRES_DB`: The database name to use for this application. You can leave the default of `app`. -* `SENTRY_DSN`: The DSN for Sentry, if you are using it. - -## GitHub Actions Environment Variables - -There are some environment variables only used by GitHub Actions that you can configure: - -* `LATEST_CHANGES`: Used by the GitHub Action [latest-changes](https://github.com/tiangolo/latest-changes) to automatically add release notes based on the PRs merged. It's a personal access token, read the docs for details. -* `SMOKESHOW_AUTH_KEY`: Used to handle and publish the code coverage using [Smokeshow](https://github.com/samuelcolvin/smokeshow), follow their instructions to create a (free) Smokeshow key. - -### Generate secret keys - -Some environment variables in the `.env` file have a default value of `changethis`. - -You have to change them with a secret key, to generate secret keys you can run the following command: - -```bash -python -c "import secrets; print(secrets.token_urlsafe(32))" -``` - -Copy the content and use that as password / secret key. And run that again to generate another secure key. - -### Deploy with Docker Compose - -With the environment variables in place, you can deploy with Docker Compose: - -```bash -docker compose -f docker-compose.yml up -d -``` - -For production you wouldn't want to have the overrides in `docker-compose.override.yml`, that's why we explicitly specify `docker-compose.yml` as the file to use. - -## Continuous Deployment (CD) - -You can use GitHub Actions to deploy your project automatically. 😎 - -You can have multiple environment deployments. - -There are already two environments configured, `staging` and `production`. πŸš€ - -### Install GitHub Actions Runner - -* On your remote server, create a user for your GitHub Actions: - -```bash -sudo adduser github -``` - -* Add Docker permissions to the `github` user: - -```bash -sudo usermod -aG docker github -``` - -* Temporarily switch to the `github` user: - -```bash -sudo su - github -``` - -* Go to the `github` user's home directory: - -```bash -cd -``` - -* [Install a GitHub Action self-hosted runner following the official guide](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners#adding-a-self-hosted-runner-to-a-repository). - -* When asked about labels, add a label for the environment, e.g. `production`. You can also add labels later. - -After installing, the guide would tell you to run a command to start the runner. Nevertheless, it would stop once you terminate that process or if your local connection to your server is lost. - -To make sure it runs on startup and continues running, you can install it as a service. To do that, exit the `github` user and go back to the `root` user: - -```bash -exit -``` - -After you do it, you will be on the previous user again. And you will be on the previous directory, belonging to that user. - -Before being able to go the `github` user directory, you need to become the `root` user (you might already be): - -```bash -sudo su -``` - -* As the `root` user, go to the `actions-runner` directory inside of the `github` user's home directory: - -```bash -cd /home/github/actions-runner -``` - -* Install the self-hosted runner as a service with the user `github`: - -```bash -./svc.sh install github -``` - -* Start the service: - -```bash -./svc.sh start -``` - -* Check the status of the service: - -```bash -./svc.sh status -``` - -You can read more about it in the official guide: [Configuring the self-hosted runner application as a service](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service). - -### Set Secrets - -On your repository, configure secrets for the environment variables you need, the same ones described above, including `SECRET_KEY`, etc. Follow the [official GitHub guide for setting repository secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository). - -The current Github Actions workflows expect these secrets: - -* `DOMAIN_PRODUCTION` -* `DOMAIN_STAGING` -* `STACK_NAME_PRODUCTION` -* `STACK_NAME_STAGING` -* `EMAILS_FROM_EMAIL` -* `FIRST_SUPERUSER` -* `FIRST_SUPERUSER_PASSWORD` -* `POSTGRES_PASSWORD` -* `SECRET_KEY` -* `LATEST_CHANGES` -* `SMOKESHOW_AUTH_KEY` - -## GitHub Action Deployment Workflows - -There are GitHub Action workflows in the `.github/workflows` directory already configured for deploying to the environments (GitHub Actions runners with the labels): - -* `staging`: after pushing (or merging) to the branch `master`. -* `production`: after publishing a release. - -If you need to add extra environments you could use those as a starting point. - -## URLs - -Replace `fastapi-project.example.com` with your domain. - -### Main Traefik Dashboard - -Traefik UI: `https://traefik.fastapi-project.example.com` - -### Production - -Frontend: `https://dashboard.fastapi-project.example.com` - -Backend API docs: `https://api.fastapi-project.example.com/docs` - -Backend API base URL: `https://api.fastapi-project.example.com` - -Adminer: `https://adminer.fastapi-project.example.com` - -### Staging - -Frontend: `https://dashboard.staging.fastapi-project.example.com` - -Backend API docs: `https://api.staging.fastapi-project.example.com/docs` - -Backend API base URL: `https://api.staging.fastapi-project.example.com` - -Adminer: `https://adminer.staging.fastapi-project.example.com` diff --git a/development.md b/development.md deleted file mode 100644 index d7d41d73f1..0000000000 --- a/development.md +++ /dev/null @@ -1,207 +0,0 @@ -# FastAPI Project - Development - -## Docker Compose - -* Start the local stack with Docker Compose: - -```bash -docker compose watch -``` - -* Now you can open your browser and interact with these URLs: - -Frontend, built with Docker, with routes handled based on the path: http://localhost:5173 - -Backend, JSON based web API based on OpenAPI: http://localhost:8000 - -Automatic interactive documentation with Swagger UI (from the OpenAPI backend): http://localhost:8000/docs - -Adminer, database web administration: http://localhost:8080 - -Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090 - -**Note**: The first time you start your stack, it might take a minute for it to be ready. While the backend waits for the database to be ready and configures everything. You can check the logs to monitor it. - -To check the logs, run (in another terminal): - -```bash -docker compose logs -``` - -To check the logs of a specific service, add the name of the service, e.g.: - -```bash -docker compose logs backend -``` - -## Local Development - -The Docker Compose files are configured so that each of the services is available in a different port in `localhost`. - -For the backend and frontend, they use the same port that would be used by their local development server, so, the backend is at `http://localhost:8000` and the frontend at `http://localhost:5173`. - -This way, you could turn off a Docker Compose service and start its local development service, and everything would keep working, because it all uses the same ports. - -For example, you can stop that `frontend` service in the Docker Compose, in another terminal, run: - -```bash -docker compose stop frontend -``` - -And then start the local frontend development server: - -```bash -cd frontend -npm run dev -``` - -Or you could stop the `backend` Docker Compose service: - -```bash -docker compose stop backend -``` - -And then you can run the local development server for the backend: - -```bash -cd backend -fastapi dev app/main.py -``` - -## Docker Compose in `localhost.tiangolo.com` - -When you start the Docker Compose stack, it uses `localhost` by default, with different ports for each service (backend, frontend, adminer, etc). - -When you deploy it to production (or staging), it will deploy each service in a different subdomain, like `api.example.com` for the backend and `dashboard.example.com` for the frontend. - -In the guide about [deployment](deployment.md) you can read about Traefik, the configured proxy. That's the component in charge of transmitting traffic to each service based on the subdomain. - -If you want to test that it's all working locally, you can edit the local `.env` file, and change: - -```dotenv -DOMAIN=localhost.tiangolo.com -``` - -That will be used by the Docker Compose files to configure the base domain for the services. - -Traefik will use this to transmit traffic at `api.localhost.tiangolo.com` to the backend, and traffic at `dashboard.localhost.tiangolo.com` to the frontend. - -The domain `localhost.tiangolo.com` is a special domain that is configured (with all its subdomains) to point to `127.0.0.1`. This way you can use that for your local development. - -After you update it, run again: - -```bash -docker compose watch -``` - -When deploying, for example in production, the main Traefik is configured outside of the Docker Compose files. For local development, there's an included Traefik in `docker-compose.override.yml`, just to let you test that the domains work as expected, for example with `api.localhost.tiangolo.com` and `dashboard.localhost.tiangolo.com`. - -## Docker Compose files and env vars - -There is a main `docker-compose.yml` file with all the configurations that apply to the whole stack, it is used automatically by `docker compose`. - -And there's also a `docker-compose.override.yml` with overrides for development, for example to mount the source code as a volume. It is used automatically by `docker compose` to apply overrides on top of `docker-compose.yml`. - -These Docker Compose files use the `.env` file containing configurations to be injected as environment variables in the containers. - -They also use some additional configurations taken from environment variables set in the scripts before calling the `docker compose` command. - -After changing variables, make sure you restart the stack: - -```bash -docker compose watch -``` - -## The .env file - -The `.env` file is the one that contains all your configurations, generated keys and passwords, etc. - -Depending on your workflow, you could want to exclude it from Git, for example if your project is public. In that case, you would have to make sure to set up a way for your CI tools to obtain it while building or deploying your project. - -One way to do it could be to add each environment variable to your CI/CD system, and updating the `docker-compose.yml` file to read that specific env var instead of reading the `.env` file. - -## Pre-commits and code linting - -we are using a tool called [pre-commit](https://pre-commit.com/) for code linting and formatting. - -When you install it, it runs right before making a commit in git. This way it ensures that the code is consistent and formatted even before it is committed. - -You can find a file `.pre-commit-config.yaml` with configurations at the root of the project. - -#### Install pre-commit to run automatically - -`pre-commit` is already part of the dependencies of the project, but you could also install it globally if you prefer to, following [the official pre-commit docs](https://pre-commit.com/). - -After having the `pre-commit` tool installed and available, you need to "install" it in the local repository, so that it runs automatically before each commit. - -Using `uv`, you could do it with: - -```bash -❯ uv run pre-commit install -pre-commit installed at .git/hooks/pre-commit -``` - -Now whenever you try to commit, e.g. with: - -```bash -git commit -``` - -...pre-commit will run and check and format the code you are about to commit, and will ask you to add that code (stage it) with git again before committing. - -Then you can `git add` the modified/fixed files again and now you can commit. - -#### Running pre-commit hooks manually - -you can also run `pre-commit` manually on all the files, you can do it using `uv` with: - -```bash -❯ uv run pre-commit run --all-files -check for added large files..............................................Passed -check toml...............................................................Passed -check yaml...............................................................Passed -ruff.....................................................................Passed -ruff-format..............................................................Passed -eslint...................................................................Passed -prettier.................................................................Passed -``` - -## URLs - -The production or staging URLs would use these same paths, but with your own domain. - -### Development URLs - -Development URLs, for local development. - -Frontend: http://localhost:5173 - -Backend: http://localhost:8000 - -Automatic Interactive Docs (Swagger UI): http://localhost:8000/docs - -Automatic Alternative Docs (ReDoc): http://localhost:8000/redoc - -Adminer: http://localhost:8080 - -Traefik UI: http://localhost:8090 - -MailCatcher: http://localhost:1080 - -### Development URLs with `localhost.tiangolo.com` Configured - -Development URLs, for local development. - -Frontend: http://dashboard.localhost.tiangolo.com - -Backend: http://api.localhost.tiangolo.com - -Automatic Interactive Docs (Swagger UI): http://api.localhost.tiangolo.com/docs - -Automatic Alternative Docs (ReDoc): http://api.localhost.tiangolo.com/redoc - -Adminer: http://localhost.tiangolo.com:8080 - -Traefik UI: http://localhost.tiangolo.com:8090 - -MailCatcher: http://localhost.tiangolo.com:1080 \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml deleted file mode 100644 index 0751abe901..0000000000 --- a/docker-compose.override.yml +++ /dev/null @@ -1,133 +0,0 @@ -services: - - # Local services are available on their ports, but also available on: - # http://api.localhost.tiangolo.com: backend - # http://dashboard.localhost.tiangolo.com: frontend - # etc. To enable it, update .env, set: - # DOMAIN=localhost.tiangolo.com - proxy: - image: traefik:3.0 - volumes: - - /var/run/docker.sock:/var/run/docker.sock - ports: - - "80:80" - - "8090:8080" - # Duplicate the command from docker-compose.yml to add --api.insecure=true - command: - # Enable Docker in Traefik, so that it reads labels from Docker services - - --providers.docker - # Add a constraint to only use services with the label for this stack - - --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`) - # Do not expose all Docker services, only the ones explicitly exposed - - --providers.docker.exposedbydefault=false - # Create an entrypoint "http" listening on port 80 - - --entrypoints.http.address=:80 - # Create an entrypoint "https" listening on port 443 - - --entrypoints.https.address=:443 - # Enable the access log, with HTTP requests - - --accesslog - # Enable the Traefik log, for configurations and errors - - --log - # Enable debug logging for local development - - --log.level=DEBUG - # Enable the Dashboard and API - - --api - # Enable the Dashboard and API in insecure mode for local development - - --api.insecure=true - labels: - # Enable Traefik for this service, to make it available in the public network - - traefik.enable=true - - traefik.constraint-label=traefik-public - # Dummy https-redirect middleware that doesn't really redirect, only to - # allow running it locally - - traefik.http.middlewares.https-redirect.contenttype.autodetect=false - networks: - - traefik-public - - default - - db: - restart: "no" - ports: - - "5432:5432" - - adminer: - restart: "no" - ports: - - "8080:8080" - - backend: - restart: "no" - ports: - - "8000:8000" - build: - context: ./backend - # command: sleep infinity # Infinite loop to keep container alive doing nothing - command: - - fastapi - - run - - --reload - - "app/main.py" - develop: - watch: - - path: ./backend - action: sync - target: /app - ignore: - - ./backend/.venv - - .venv - - path: ./backend/pyproject.toml - action: rebuild - # TODO: remove once coverage is done locally - volumes: - - ./backend/htmlcov:/app/htmlcov - environment: - SMTP_HOST: "mailcatcher" - SMTP_PORT: "1025" - SMTP_TLS: "false" - EMAILS_FROM_EMAIL: "noreply@example.com" - - mailcatcher: - image: schickling/mailcatcher - ports: - - "1080:1080" - - "1025:1025" - - frontend: - restart: "no" - ports: - - "5173:80" - build: - context: ./frontend - args: - - VITE_API_URL=http://localhost:8000 - - NODE_ENV=development - - playwright: - build: - context: ./frontend - dockerfile: Dockerfile.playwright - args: - - VITE_API_URL=http://backend:8000 - - NODE_ENV=production - ipc: host - depends_on: - - backend - - mailcatcher - env_file: - - .env - environment: - - VITE_API_URL=http://backend:8000 - - MAILCATCHER_HOST=http://mailcatcher:1080 - # For the reports when run locally - - PLAYWRIGHT_HTML_HOST=0.0.0.0 - - CI=${CI} - volumes: - - ./frontend/blob-report:/app/blob-report - - ./frontend/test-results:/app/test-results - ports: - - 9323:9323 - -networks: - traefik-public: - # For local dev, don't expect an external Traefik network - external: false diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml deleted file mode 100644 index 886d6dcc2f..0000000000 --- a/docker-compose.traefik.yml +++ /dev/null @@ -1,77 +0,0 @@ -services: - traefik: - image: traefik:3.0 - ports: - # Listen on port 80, default for HTTP, necessary to redirect to HTTPS - - 80:80 - # Listen on port 443, default for HTTPS - - 443:443 - restart: always - labels: - # Enable Traefik for this service, to make it available in the public network - - traefik.enable=true - # Use the traefik-public network (declared below) - - traefik.docker.network=traefik-public - # Define the port inside of the Docker service to use - - traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080 - # Make Traefik use this domain (from an environment variable) in HTTP - - traefik.http.routers.traefik-dashboard-http.entrypoints=http - - traefik.http.routers.traefik-dashboard-http.rule=Host(`traefik.${DOMAIN?Variable not set}`) - # traefik-https the actual router using HTTPS - - traefik.http.routers.traefik-dashboard-https.entrypoints=https - - traefik.http.routers.traefik-dashboard-https.rule=Host(`traefik.${DOMAIN?Variable not set}`) - - traefik.http.routers.traefik-dashboard-https.tls=true - # Use the "le" (Let's Encrypt) resolver created below - - traefik.http.routers.traefik-dashboard-https.tls.certresolver=le - # Use the special Traefik service api@internal with the web UI/Dashboard - - traefik.http.routers.traefik-dashboard-https.service=api@internal - # https-redirect middleware to redirect HTTP to HTTPS - - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https - - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true - # traefik-http set up only to use the middleware to redirect to https - - traefik.http.routers.traefik-dashboard-http.middlewares=https-redirect - # admin-auth middleware with HTTP Basic auth - # Using the environment variables USERNAME and HASHED_PASSWORD - - traefik.http.middlewares.admin-auth.basicauth.users=${USERNAME?Variable not set}:${HASHED_PASSWORD?Variable not set} - # Enable HTTP Basic auth, using the middleware created above - - traefik.http.routers.traefik-dashboard-https.middlewares=admin-auth - volumes: - # Add Docker as a mounted volume, so that Traefik can read the labels of other services - - /var/run/docker.sock:/var/run/docker.sock:ro - # Mount the volume to store the certificates - - traefik-public-certificates:/certificates - command: - # Enable Docker in Traefik, so that it reads labels from Docker services - - --providers.docker - # Do not expose all Docker services, only the ones explicitly exposed - - --providers.docker.exposedbydefault=false - # Create an entrypoint "http" listening on port 80 - - --entrypoints.http.address=:80 - # Create an entrypoint "https" listening on port 443 - - --entrypoints.https.address=:443 - # Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL - - --certificatesresolvers.le.acme.email=${EMAIL?Variable not set} - # Store the Let's Encrypt certificates in the mounted volume - - --certificatesresolvers.le.acme.storage=/certificates/acme.json - # Use the TLS Challenge for Let's Encrypt - - --certificatesresolvers.le.acme.tlschallenge=true - # Enable the access log, with HTTP requests - - --accesslog - # Enable the Traefik log, for configurations and errors - - --log - # Enable the Dashboard and API - - --api - networks: - # Use the public network created to be shared between Traefik and - # any other service that needs to be publicly available with HTTPS - - traefik-public - -volumes: - # Create a volume to store the certificates, even if the container is recreated - traefik-public-certificates: - -networks: - # Use the previously created public network "traefik-public", shared with other - # services that need to be publicly available via this Traefik - traefik-public: - external: true diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c92d5d4451..0000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,171 +0,0 @@ -services: - - db: - image: postgres:12 - restart: always - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] - interval: 10s - retries: 5 - start_period: 30s - timeout: 10s - volumes: - - app-db-data:/var/lib/postgresql/data/pgdata - env_file: - - .env - environment: - - PGDATA=/var/lib/postgresql/data/pgdata - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} - - POSTGRES_USER=${POSTGRES_USER?Variable not set} - - POSTGRES_DB=${POSTGRES_DB?Variable not set} - - adminer: - image: adminer - restart: always - networks: - - traefik-public - - default - depends_on: - - db - environment: - - ADMINER_DESIGN=pepa-linha-dark - labels: - - traefik.enable=true - - traefik.docker.network=traefik-public - - traefik.constraint-label=traefik-public - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-http.rule=Host(`adminer.${DOMAIN?Variable not set}`) - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-http.entrypoints=http - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-http.middlewares=https-redirect - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.rule=Host(`adminer.${DOMAIN?Variable not set}`) - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.entrypoints=https - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.tls=true - - traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.tls.certresolver=le - - traefik.http.services.${STACK_NAME?Variable not set}-adminer.loadbalancer.server.port=8080 - - prestart: - image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}' - build: - context: ./backend - networks: - - traefik-public - - default - depends_on: - db: - condition: service_healthy - restart: true - command: bash scripts/prestart.sh - env_file: - - .env - environment: - - DOMAIN=${DOMAIN} - - FRONTEND_HOST=${FRONTEND_HOST?Variable not set} - - ENVIRONMENT=${ENVIRONMENT} - - BACKEND_CORS_ORIGINS=${BACKEND_CORS_ORIGINS} - - SECRET_KEY=${SECRET_KEY?Variable not set} - - FIRST_SUPERUSER=${FIRST_SUPERUSER?Variable not set} - - FIRST_SUPERUSER_PASSWORD=${FIRST_SUPERUSER_PASSWORD?Variable not set} - - SMTP_HOST=${SMTP_HOST} - - SMTP_USER=${SMTP_USER} - - SMTP_PASSWORD=${SMTP_PASSWORD} - - EMAILS_FROM_EMAIL=${EMAILS_FROM_EMAIL} - - POSTGRES_SERVER=db - - POSTGRES_PORT=${POSTGRES_PORT} - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_USER=${POSTGRES_USER?Variable not set} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} - - SENTRY_DSN=${SENTRY_DSN} - - backend: - image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}' - restart: always - networks: - - traefik-public - - default - depends_on: - db: - condition: service_healthy - restart: true - prestart: - condition: service_completed_successfully - env_file: - - .env - environment: - - DOMAIN=${DOMAIN} - - FRONTEND_HOST=${FRONTEND_HOST?Variable not set} - - ENVIRONMENT=${ENVIRONMENT} - - BACKEND_CORS_ORIGINS=${BACKEND_CORS_ORIGINS} - - SECRET_KEY=${SECRET_KEY?Variable not set} - - FIRST_SUPERUSER=${FIRST_SUPERUSER?Variable not set} - - FIRST_SUPERUSER_PASSWORD=${FIRST_SUPERUSER_PASSWORD?Variable not set} - - SMTP_HOST=${SMTP_HOST} - - SMTP_USER=${SMTP_USER} - - SMTP_PASSWORD=${SMTP_PASSWORD} - - EMAILS_FROM_EMAIL=${EMAILS_FROM_EMAIL} - - POSTGRES_SERVER=db - - POSTGRES_PORT=${POSTGRES_PORT} - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_USER=${POSTGRES_USER?Variable not set} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} - - SENTRY_DSN=${SENTRY_DSN} - - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/utils/health-check/"] - interval: 10s - timeout: 5s - retries: 5 - - build: - context: ./backend - labels: - - traefik.enable=true - - traefik.docker.network=traefik-public - - traefik.constraint-label=traefik-public - - - traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=8000 - - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=Host(`api.${DOMAIN?Variable not set}`) - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.entrypoints=http - - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.rule=Host(`api.${DOMAIN?Variable not set}`) - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.entrypoints=https - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls=true - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls.certresolver=le - - # Enable redirection for HTTP and HTTPS - - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.middlewares=https-redirect - - frontend: - image: '${DOCKER_IMAGE_FRONTEND?Variable not set}:${TAG-latest}' - restart: always - networks: - - traefik-public - - default - build: - context: ./frontend - args: - - VITE_API_URL=https://api.${DOMAIN?Variable not set} - - NODE_ENV=production - labels: - - traefik.enable=true - - traefik.docker.network=traefik-public - - traefik.constraint-label=traefik-public - - - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 - - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=Host(`dashboard.${DOMAIN?Variable not set}`) - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.entrypoints=http - - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.rule=Host(`dashboard.${DOMAIN?Variable not set}`) - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.entrypoints=https - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls=true - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls.certresolver=le - - # Enable redirection for HTTP and HTTPS - - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.middlewares=https-redirect -volumes: - app-db-data: - -networks: - traefik-public: - # Allow setting it to false for testing - external: true diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 8728c7b029..66fad8f21f 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,23 +1,52 @@ # Stage 0, "build-stage", based on Node.js, to build and compile the frontend -FROM node:20 AS build-stage +FROM node:20-slim AS build-stage WORKDIR /app -COPY package*.json /app/ +# Copy package files first for better caching +COPY package*.json ./ +# Install dependencies RUN npm install -COPY ./ /app/ +# Copy all source files +COPY . . -ARG VITE_API_URL=${VITE_API_URL} +# Set the API URL for the build +ARG VITE_API_URL=http://localhost:8000 +ENV VITE_API_URL=${VITE_API_URL} +# Build the application RUN npm run build - # Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx -FROM nginx:1 +FROM nginx:1.25-alpine + +# Remove default nginx configuration +RUN rm -rf /etc/nginx/conf.d/* +# Create necessary directories +RUN mkdir -p /etc/nginx/extra-conf.d + +# Copy the built app COPY --from=build-stage /app/dist/ /usr/share/nginx/html +# Copy nginx configuration COPY ./nginx.conf /etc/nginx/conf.d/default.conf -COPY ./nginx-backend-not-found.conf /etc/nginx/extra-conf.d/backend-not-found.conf + +# Set proper permissions for nginx +RUN chown -R nginx:nginx /usr/share/nginx/html && \ + chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + chown -R nginx:nginx /etc/nginx/conf.d && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid + +# Switch to non-root user +USER nginx + +# Expose port 80 +EXPOSE 80 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/kubernetes/charts/SDP/Chart.yaml b/kubernetes/charts/SDP/Chart.yaml new file mode 100644 index 0000000000..0818bfed04 --- /dev/null +++ b/kubernetes/charts/SDP/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: sdp +description: A Helm chart for deploying the FastAPI fullstack application +type: application +version: 0.1.0 +appVersion: "1.0.0" \ No newline at end of file diff --git a/kubernetes/charts/SDP/templates/adminer-deployment.yaml b/kubernetes/charts/SDP/templates/adminer-deployment.yaml new file mode 100644 index 0000000000..8fb29310ba --- /dev/null +++ b/kubernetes/charts/SDP/templates/adminer-deployment.yaml @@ -0,0 +1,35 @@ +# Adminer Deployment +{{- if .Values.adminer.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-adminer + labels: + app: adminer +spec: + replicas: {{ .Values.adminer.replicaCount }} + selector: + matchLabels: + app: adminer + template: + metadata: + labels: + app: adminer + spec: + containers: + - name: adminer + image: "{{ .Values.adminer.image.repository }}:{{ .Values.adminer.image.tag }}" + imagePullPolicy: {{ .Values.adminer.image.pullPolicy }} + ports: + - containerPort: {{ .Values.adminer.service.port }} + volumeMounts: + {{- range .Values.adminer.volumeMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- end }} + volumes: + {{- range .Values.adminer.volumes }} + - name: {{ .name }} + emptyDir: {} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/charts/SDP/templates/adminer-service.yaml b/kubernetes/charts/SDP/templates/adminer-service.yaml new file mode 100644 index 0000000000..b5e07371d8 --- /dev/null +++ b/kubernetes/charts/SDP/templates/adminer-service.yaml @@ -0,0 +1,16 @@ +# Adminer Service +{{- if .Values.adminer.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-adminer + labels: + app: adminer +spec: + type: {{ .Values.adminer.service.type }} + ports: + - port: {{ .Values.adminer.service.port }} + targetPort: {{ .Values.adminer.service.port }} + selector: + app: adminer +{{- end }} \ No newline at end of file diff --git a/kubernetes/charts/SDP/templates/backend-deployment.yaml b/kubernetes/charts/SDP/templates/backend-deployment.yaml new file mode 100644 index 0000000000..3de32e028c --- /dev/null +++ b/kubernetes/charts/SDP/templates/backend-deployment.yaml @@ -0,0 +1,80 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-backend + labels: + app.kubernetes.io/name: {{ .Release.Name }}-backend + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + replicas: {{ .Values.backend.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/name: {{ .Release.Name }}-backend + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ .Release.Name }}-backend + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + initContainers: + - name: init-db + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}" + command: ["sh", "-c", "mkdir -p /app/data && chmod 777 /app/data"] + securityContext: + runAsUser: 0 + volumeMounts: + - name: sqlite-data + mountPath: /app/data + containers: + - name: backend + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}" + imagePullPolicy: {{ .Values.backend.image.pullPolicy }} + ports: + - name: http + containerPort: 8000 + protocol: TCP + resources: + {{- toYaml .Values.backend.resources | nindent 12 }} + env: + {{- range $key, $value := .Values.backend.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + - name: PYTHONPATH + value: "/app" + volumeMounts: + - name: sqlite-data + mountPath: /app/data + command: ["sh", "-c"] + args: + - | + echo "Current directory: $(pwd)" && + echo "Python path: $PYTHONPATH" && + echo "Contents of /app: $(ls -la /app)" && + echo "Python version: $(python3.11 --version)" && + python3.11 -m app.backend_pre_start && + python3.11 -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --log-level debug + startupProbe: + httpGet: + path: /api/v1/utils/health-check/ + port: http + failureThreshold: 30 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /api/v1/utils/health-check/ + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /api/v1/utils/health-check/ + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + failureThreshold: 3 + volumes: + - name: sqlite-data + emptyDir: {} \ No newline at end of file diff --git a/kubernetes/charts/SDP/templates/backend-service.yaml b/kubernetes/charts/SDP/templates/backend-service.yaml new file mode 100644 index 0000000000..199d656e34 --- /dev/null +++ b/kubernetes/charts/SDP/templates/backend-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-backend + labels: + app.kubernetes.io/name: {{ .Release.Name }}-backend + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + type: {{ .Values.backend.service.type }} + ports: + - port: {{ .Values.backend.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ .Release.Name }}-backend + app.kubernetes.io/instance: {{ .Release.Name }} \ No newline at end of file diff --git a/kubernetes/charts/SDP/templates/frontend-deployment.yaml b/kubernetes/charts/SDP/templates/frontend-deployment.yaml new file mode 100644 index 0000000000..23eb201ad1 --- /dev/null +++ b/kubernetes/charts/SDP/templates/frontend-deployment.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-frontend + labels: + app: {{ .Release.Name }}-frontend +spec: + replicas: {{ .Values.frontend.replicaCount }} + selector: + matchLabels: + app: {{ .Release.Name }}-frontend + template: + metadata: + labels: + app: {{ .Release.Name }}-frontend + spec: + containers: + - name: frontend + image: {{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }} + imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} + ports: + - containerPort: 80 + resources: + {{- toYaml .Values.frontend.resources | nindent 12 }} + env: + {{- range $key, $value := .Values.frontend.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} \ No newline at end of file diff --git a/kubernetes/charts/SDP/templates/frontend-service.yaml b/kubernetes/charts/SDP/templates/frontend-service.yaml new file mode 100644 index 0000000000..c52af00811 --- /dev/null +++ b/kubernetes/charts/SDP/templates/frontend-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-frontend + labels: + app: {{ .Release.Name }}-frontend +spec: + type: {{ .Values.frontend.service.type }} + ports: + - port: {{ .Values.frontend.service.port }} + targetPort: 80 + protocol: TCP + name: http + selector: + app: {{ .Release.Name }}-frontend \ No newline at end of file diff --git a/kubernetes/charts/SDP/values.yaml b/kubernetes/charts/SDP/values.yaml new file mode 100644 index 0000000000..17f052d7d9 --- /dev/null +++ b/kubernetes/charts/SDP/values.yaml @@ -0,0 +1,81 @@ +# Global values +global: + environment: production + +# Backend configuration +backend: + image: + repository: dperezsa/sdp-backend + tag: 1.0.0 + pullPolicy: IfNotPresent + replicaCount: 1 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 200m + memory: 256Mi + service: + type: ClusterIP + port: 8000 + env: + PROJECT_NAME: Service Data Platform + ENVIRONMENT: production + FIRST_SUPERUSER: admin@example.com + FIRST_SUPERUSER_PASSWORD: changeme123 + FRONTEND_HOST: http://localhost:5173 + SECRET_KEY: changeme123 + SQLITE_DB: ./sqlite.db + volumeMounts: + - name: sqlite-data + mountPath: /app/data + volumes: + - name: sqlite-data + emptyDir: {} + +# Frontend configuration +frontend: + image: + repository: dperezsa/sdp-frontend + tag: 1.0.0 + pullPolicy: IfNotPresent + replicaCount: 1 + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi + service: + type: ClusterIP + port: 80 + env: + VITE_API_URL: http://{{ .Release.Name }}-backend.default.svc.cluster.local:8000 + +# Adminer configuration +adminer: + enabled: false + image: + repository: dperezsa/adminer + tag: latest + pullPolicy: IfNotPresent + replicaCount: 1 + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi + service: + type: ClusterIP + port: 80 + env: {} + volumeMounts: + - name: sqlite-data + mountPath: /var/www/html/data + volumes: + - name: sqlite-data + emptyDir: {} \ No newline at end of file diff --git a/release-notes.md b/release-notes.md deleted file mode 100644 index 5420a98d89..0000000000 --- a/release-notes.md +++ /dev/null @@ -1,568 +0,0 @@ -# Release Notes - -## Latest Changes - -### Fixes - -* πŸ› Close sidebar drawer on user selection. PR [#1515](https://github.com/fastapi/full-stack-fastapi-template/pull/1515) by [@dtellz](https://github.com/dtellz). -* πŸ› Fix required password validation when editing user fields. PR [#1508](https://github.com/fastapi/full-stack-fastapi-template/pull/1508) by [@jpizquierdo](https://github.com/jpizquierdo). - -### Refactors - -* ♻️ Remove redundant field in inherited class. PR [#1520](https://github.com/fastapi/full-stack-fastapi-template/pull/1520) by [@tzway](https://github.com/tzway). -* 🎨 Add minor UI tweaks in Skeletons and other components. PR [#1507](https://github.com/fastapi/full-stack-fastapi-template/pull/1507) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Add minor UI tweaks. PR [#1506](https://github.com/fastapi/full-stack-fastapi-template/pull/1506) by [@alejsdev](https://github.com/alejsdev). - -### Internal - -* πŸ”§ Add npm and docker package ecosystems to Dependabot configuration. PR [#1535](https://github.com/fastapi/full-stack-fastapi-template/pull/1535) by [@alejsdev](https://github.com/alejsdev). - -## 0.8.0 - -### Features - -* πŸ›‚ Migrate to Chakra UI v3 . PR [#1496](https://github.com/fastapi/full-stack-fastapi-template/pull/1496) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add private, local only, API for usage in E2E tests. PR [#1429](https://github.com/fastapi/full-stack-fastapi-template/pull/1429) by [@patrick91](https://github.com/patrick91). -* ✨ Migrate to latest openapi-ts. PR [#1430](https://github.com/fastapi/full-stack-fastapi-template/pull/1430) by [@patrick91](https://github.com/patrick91). - -### Fixes - -* πŸ§‘β€πŸ”§ Replace correct value for 'htmlFor'. PR [#1456](https://github.com/fastapi/full-stack-fastapi-template/pull/1456) by [@wesenbergg](https://github.com/wesenbergg). - -### Refactors - -* ♻️ Redirect the user to `login` if we get 401/403. PR [#1501](https://github.com/fastapi/full-stack-fastapi-template/pull/1501) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Refactor reset password test to create normal user instead of using super user. PR [#1499](https://github.com/fastapi/full-stack-fastapi-template/pull/1499) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Replace email types from `str` to `EmailStr` in `config.py`. PR [#1492](https://github.com/fastapi/full-stack-fastapi-template/pull/1492) by [@jpizquierdo](https://github.com/jpizquierdo). -* πŸ”§ Remove unused context from router creation. PR [#1498](https://github.com/fastapi/full-stack-fastapi-template/pull/1498) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Remove redundant item deletion code leveraging cascade delete. PR [#1481](https://github.com/fastapi/full-stack-fastapi-template/pull/1481) by [@nauanbek](https://github.com/nauanbek). -* ✏️ Fix a couple of spelling mistakes. PR [#1485](https://github.com/fastapi/full-stack-fastapi-template/pull/1485) by [@rjmunro](https://github.com/rjmunro). -* 🎨 Move `prefix` and `tags` to routers. PR [#1439](https://github.com/fastapi/full-stack-fastapi-template/pull/1439) by [@patrick91](https://github.com/patrick91). -* ♻️ Remove modify id script in favor of openapi-ts config. PR [#1434](https://github.com/fastapi/full-stack-fastapi-template/pull/1434) by [@patrick91](https://github.com/patrick91). -* πŸ‘· Improve Playwright CI speed: sharding (parallel runs), run in Docker to use cache, use env vars. PR [#1405](https://github.com/fastapi/full-stack-fastapi-template/pull/1405) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Add PaginationFooter component. PR [#1381](https://github.com/fastapi/full-stack-fastapi-template/pull/1381) by [@saltie2193](https://github.com/saltie2193). -* ♻️ Refactored code to use encryption algorithm name from settings for consistency. PR [#1160](https://github.com/fastapi/full-stack-fastapi-template/pull/1160) by [@sameeramin](https://github.com/sameeramin). -* πŸ”Š Enable logging for email utils by default. PR [#1374](https://github.com/fastapi/full-stack-fastapi-template/pull/1374) by [@ihmily](https://github.com/ihmily). -* πŸ”§ Add `ENV PYTHONUNBUFFERED=1` to log output directly to Docker. PR [#1378](https://github.com/fastapi/full-stack-fastapi-template/pull/1378) by [@tiangolo](https://github.com/tiangolo). -* πŸ’‘ Remove unnecessary comment. PR [#1260](https://github.com/fastapi/full-stack-fastapi-template/pull/1260) by [@sebhani](https://github.com/sebhani). - -### Upgrades - -* ⬆️ Update Dockerfile to use uv version 0.5.11. PR [#1454](https://github.com/fastapi/full-stack-fastapi-template/pull/1454) by [@alejsdev](https://github.com/alejsdev). - -### Docs - -* πŸ“ Removing deprecated manual client SDK step. PR [#1494](https://github.com/fastapi/full-stack-fastapi-template/pull/1494) by [@chandy](https://github.com/chandy). -* πŸ“ Update Frontend README.md. PR [#1462](https://github.com/fastapi/full-stack-fastapi-template/pull/1462) by [@getmarkus](https://github.com/getmarkus). -* πŸ“ Update `frontend/README.md` to also remove Playwright when removing Frontend. PR [#1452](https://github.com/fastapi/full-stack-fastapi-template/pull/1452) by [@youben11](https://github.com/youben11). -* πŸ“ Update `deployment.md`, instructions to install GitHub Runner in non-root VMs. PR [#1412](https://github.com/fastapi/full-stack-fastapi-template/pull/1412) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Add MailCatcher to `development.md`. PR [#1387](https://github.com/fastapi/full-stack-fastapi-template/pull/1387) by [@tobiase](https://github.com/tobiase). - -### Internal - -* πŸ”§ Configure path alias for cleaner imports. PR [#1497](https://github.com/fastapi/full-stack-fastapi-template/pull/1497) by [@alejsdev](https://github.com/alejsdev). -* Bump vite from 5.0.13 to 5.4.14 in /frontend. PR [#1469](https://github.com/fastapi/full-stack-fastapi-template/pull/1469) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Bump astral-sh/setup-uv from 4 to 5. PR [#1453](https://github.com/fastapi/full-stack-fastapi-template/pull/1453) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Bump astral-sh/setup-uv from 3 to 4. PR [#1433](https://github.com/fastapi/full-stack-fastapi-template/pull/1433) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Bump tiangolo/latest-changes from 0.3.1 to 0.3.2. PR [#1418](https://github.com/fastapi/full-stack-fastapi-template/pull/1418) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ‘· Update issue manager workflow. PR [#1398](https://github.com/fastapi/full-stack-fastapi-template/pull/1398) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Fix smokeshow, checkout files on CI. PR [#1395](https://github.com/fastapi/full-stack-fastapi-template/pull/1395) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update `labeler.yml`. PR [#1388](https://github.com/fastapi/full-stack-fastapi-template/pull/1388) by [@tiangolo](https://github.com/tiangolo). -* πŸ”§ Add .auth playwright folder to `.gitignore`. PR [#1383](https://github.com/fastapi/full-stack-fastapi-template/pull/1383) by [@justin-p](https://github.com/justin-p). -* ⬆️ Bump rollup from 4.6.1 to 4.22.5 in /frontend. PR [#1379](https://github.com/fastapi/full-stack-fastapi-template/pull/1379) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Bump astral-sh/setup-uv from 2 to 3. PR [#1364](https://github.com/fastapi/full-stack-fastapi-template/pull/1364) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ‘· Update pre-commit end-of-file-fixer hook to exclude email-templates. PR [#1296](https://github.com/fastapi/full-stack-fastapi-template/pull/1296) by [@goabonga](https://github.com/goabonga). -* ⬆ Bump tiangolo/issue-manager from 0.5.0 to 0.5.1. PR [#1332](https://github.com/fastapi/full-stack-fastapi-template/pull/1332) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ”§ Run task by the same Python environment used to run Copier. PR [#1157](https://github.com/fastapi/full-stack-fastapi-template/pull/1157) by [@waketzheng](https://github.com/waketzheng). -* πŸ‘· Tweak generate client to error out if there are errors. PR [#1377](https://github.com/fastapi/full-stack-fastapi-template/pull/1377) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Generate and commit client only on same repo PRs, on forks, show the error. PR [#1376](https://github.com/fastapi/full-stack-fastapi-template/pull/1376) by [@tiangolo](https://github.com/tiangolo). - -## 0.7.1 - -### Highlights - -* Migrate from Poetry to [`uv`](https://github.com/astral-sh/uv). -* Simplifications and improvements for Docker Compose files, Traefik Dockerfiles. -* Make the API use its own domain `api.example.com` and the frontend use `dashboard.example.com`. This would make it easier to deploy them separately if you needed that. -* The backend and frontend on Docker Compose now listen on the same port as the local development servers, this way you can stop the Docker Compose services and run the local development servers without changing the frontend configuration. - -### Features - -* 🩺 Add DB healthcheck. PR [#1342](https://github.com/fastapi/full-stack-fastapi-template/pull/1342) by [@tiangolo](https://github.com/tiangolo). - -### Refactors - -* ♻️ Update settings to use top level `.env` file. PR [#1359](https://github.com/fastapi/full-stack-fastapi-template/pull/1359) by [@tiangolo](https://github.com/tiangolo). -* ⬆️ Migrate from Poetry to uv. PR [#1356](https://github.com/fastapi/full-stack-fastapi-template/pull/1356) by [@tiangolo](https://github.com/tiangolo). -* πŸ”₯ Remove logic for development dependencies and Jupyter, it was never documented, and I no longer use that trick. PR [#1355](https://github.com/fastapi/full-stack-fastapi-template/pull/1355) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Use Docker Compose `watch`. PR [#1354](https://github.com/fastapi/full-stack-fastapi-template/pull/1354) by [@tiangolo](https://github.com/tiangolo). -* πŸ”§ Use plain base official Python Docker image. PR [#1351](https://github.com/fastapi/full-stack-fastapi-template/pull/1351) by [@tiangolo](https://github.com/tiangolo). -* 🚚 Move location of scripts to simplify file structure. PR [#1352](https://github.com/fastapi/full-stack-fastapi-template/pull/1352) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Refactor prestart (migrations), move that to its own container. PR [#1350](https://github.com/fastapi/full-stack-fastapi-template/pull/1350) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Include `FRONTEND_HOST` in CORS origins by default. PR [#1348](https://github.com/fastapi/full-stack-fastapi-template/pull/1348) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Simplify domains with `api.example.com` for API and `dashboard.example.com` for frontend, improve local development with `localhost`. PR [#1344](https://github.com/fastapi/full-stack-fastapi-template/pull/1344) by [@tiangolo](https://github.com/tiangolo). -* πŸ”₯ Simplify Traefik, remove www-redirects that add complexity. PR [#1343](https://github.com/fastapi/full-stack-fastapi-template/pull/1343) by [@tiangolo](https://github.com/tiangolo). -* πŸ”₯ Enable support for Arm Docker images in Mac, remove old patch. PR [#1341](https://github.com/fastapi/full-stack-fastapi-template/pull/1341) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Remove duplicate information in the ItemCreate model. PR [#1287](https://github.com/fastapi/full-stack-fastapi-template/pull/1287) by [@jjaakko](https://github.com/jjaakko). - -### Upgrades - -* ⬆️ Upgrade FastAPI. PR [#1349](https://github.com/fastapi/full-stack-fastapi-template/pull/1349) by [@tiangolo](https://github.com/tiangolo). - -### Docs - -* πŸ’‘ Add comments to Dockerfile with uv references. PR [#1357](https://github.com/fastapi/full-stack-fastapi-template/pull/1357) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Add Email Templates to `backend/README.md`. PR [#1311](https://github.com/fastapi/full-stack-fastapi-template/pull/1311) by [@alejsdev](https://github.com/alejsdev). - -### Internal - -* πŸ‘· Do not sync labels as it overrides manually added labels. PR [#1307](https://github.com/fastapi/full-stack-fastapi-template/pull/1307) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Use uv cache on GitHub Actions. PR [#1366](https://github.com/fastapi/full-stack-fastapi-template/pull/1366) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update GitHub Actions format. PR [#1363](https://github.com/fastapi/full-stack-fastapi-template/pull/1363) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Use `uv` for Python env to generate client. PR [#1362](https://github.com/fastapi/full-stack-fastapi-template/pull/1362) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Run tests from Python environment (with `uv`), not from Docker container. PR [#1361](https://github.com/fastapi/full-stack-fastapi-template/pull/1361) by [@tiangolo](https://github.com/tiangolo). -* πŸ”¨ Update `generate-client.sh` script, make it fail on errors, fix generation. PR [#1360](https://github.com/fastapi/full-stack-fastapi-template/pull/1360) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Add GitHub Actions workflow to lint backend apart from tests. PR [#1358](https://github.com/fastapi/full-stack-fastapi-template/pull/1358) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Improve playwright CI job. PR [#1335](https://github.com/fastapi/full-stack-fastapi-template/pull/1335) by [@patrick91](https://github.com/patrick91). -* πŸ‘· Update `issue-manager.yml`. PR [#1329](https://github.com/fastapi/full-stack-fastapi-template/pull/1329) by [@tiangolo](https://github.com/tiangolo). -* πŸ’š Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#1327](https://github.com/fastapi/full-stack-fastapi-template/pull/1327) by [@svlandeg](https://github.com/svlandeg). -* πŸ‘·πŸ» Auto-generate frontend client . PR [#1320](https://github.com/fastapi/full-stack-fastapi-template/pull/1320) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Fix in `.github/labeler.yml`. PR [#1322](https://github.com/fastapi/full-stack-fastapi-template/pull/1322) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Update `.github/labeler.yml`. PR [#1321](https://github.com/fastapi/full-stack-fastapi-template/pull/1321) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Update `latest-changes` GitHub Action. PR [#1315](https://github.com/fastapi/full-stack-fastapi-template/pull/1315) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update configs for labeler. PR [#1308](https://github.com/fastapi/full-stack-fastapi-template/pull/1308) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update GitHub Action labeler to add only one label. PR [#1304](https://github.com/fastapi/full-stack-fastapi-template/pull/1304) by [@tiangolo](https://github.com/tiangolo). -* ⬆️ Bump axios from 1.6.2 to 1.7.4 in /frontend. PR [#1301](https://github.com/fastapi/full-stack-fastapi-template/pull/1301) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ‘· Update GitHub Action labeler dependencies. PR [#1302](https://github.com/fastapi/full-stack-fastapi-template/pull/1302) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update GitHub Action labeler permissions. PR [#1300](https://github.com/fastapi/full-stack-fastapi-template/pull/1300) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Add GitHub Action label-checker. PR [#1299](https://github.com/fastapi/full-stack-fastapi-template/pull/1299) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Add GitHub Action labeler. PR [#1298](https://github.com/fastapi/full-stack-fastapi-template/pull/1298) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Add GitHub Action add-to-project. PR [#1297](https://github.com/fastapi/full-stack-fastapi-template/pull/1297) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update issue-manager. PR [#1288](https://github.com/fastapi/full-stack-fastapi-template/pull/1288) by [@tiangolo](https://github.com/tiangolo). - -## 0.7.0 - -Lots of new things! 🎁 - -* E2E tests with Playwright. -* Mailcatcher configuration, to develop and test email handling. -* Pagination. -* UUIDs for database keys. -* New user sign up. -* Support for deploying to multiple environments (staging, prod). -* Many refactors and improvements. -* Several dependency upgrades. - -### Features - -* ✨ Add User Settings e2e tests. PR [#1271](https://github.com/tiangolo/full-stack-fastapi-template/pull/1271) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Reset Password e2e tests. PR [#1270](https://github.com/tiangolo/full-stack-fastapi-template/pull/1270) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Sign Up e2e tests. PR [#1268](https://github.com/tiangolo/full-stack-fastapi-template/pull/1268) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Sign Up and make `OPEN_USER_REGISTRATION=True` by default. PR [#1265](https://github.com/tiangolo/full-stack-fastapi-template/pull/1265) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Login e2e tests. PR [#1264](https://github.com/tiangolo/full-stack-fastapi-template/pull/1264) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add initial setup for frontend / end-to-end tests with Playwright. PR [#1261](https://github.com/tiangolo/full-stack-fastapi-template/pull/1261) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add mailcatcher configuration. PR [#1244](https://github.com/tiangolo/full-stack-fastapi-template/pull/1244) by [@patrick91](https://github.com/patrick91). -* ✨ Introduce pagination in items. PR [#1239](https://github.com/tiangolo/full-stack-fastapi-template/pull/1239) by [@patrick91](https://github.com/patrick91). -* πŸ—ƒοΈ Add max_length validation for database models and input data. PR [#1233](https://github.com/tiangolo/full-stack-fastapi-template/pull/1233) by [@estebanx64](https://github.com/estebanx64). -* ✨ Add TanStack React Query devtools in dev build. PR [#1217](https://github.com/tiangolo/full-stack-fastapi-template/pull/1217) by [@tomerb](https://github.com/tomerb). -* ✨ Add support for deploying multiple environments (staging, production) to the same server. PR [#1128](https://github.com/tiangolo/full-stack-fastapi-template/pull/1128) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update CI GitHub Actions to allow running in private repos. PR [#1125](https://github.com/tiangolo/full-stack-fastapi-template/pull/1125) by [@tiangolo](https://github.com/tiangolo). - -### Fixes - -* πŸ› Fix welcome page to show logged-in user. PR [#1218](https://github.com/tiangolo/full-stack-fastapi-template/pull/1218) by [@tomerb](https://github.com/tomerb). -* πŸ› Fix local Traefik proxy network config to fix Gateway Timeouts. PR [#1184](https://github.com/tiangolo/full-stack-fastapi-template/pull/1184) by [@JoelGotsch](https://github.com/JoelGotsch). -* ♻️ Fix tests when first superuser password is changed in .env. PR [#1165](https://github.com/tiangolo/full-stack-fastapi-template/pull/1165) by [@billzhong](https://github.com/billzhong). -* πŸ› Fix bug when resetting password. PR [#1171](https://github.com/tiangolo/full-stack-fastapi-template/pull/1171) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Fix 403 when the frontend has a directory without an index.html. PR [#1094](https://github.com/tiangolo/full-stack-fastapi-template/pull/1094) by [@tiangolo](https://github.com/tiangolo). - -### Refactors - -* 🚨 Fix Docker build warning. PR [#1283](https://github.com/tiangolo/full-stack-fastapi-template/pull/1283) by [@erip](https://github.com/erip). -* ♻️ Regenerate client to use UUID instead of id integers and update frontend. PR [#1281](https://github.com/tiangolo/full-stack-fastapi-template/pull/1281) by [@rehanabdul](https://github.com/rehanabdul). -* ♻️ Tweaks in frontend. PR [#1273](https://github.com/tiangolo/full-stack-fastapi-template/pull/1273) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Add random password util and refactor tests. PR [#1277](https://github.com/tiangolo/full-stack-fastapi-template/pull/1277) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor models to use cascade delete relationships . PR [#1276](https://github.com/tiangolo/full-stack-fastapi-template/pull/1276) by [@alejsdev](https://github.com/alejsdev). -* πŸ”₯ Remove `USERS_OPEN_REGISTRATION` config, make registration enabled by default. PR [#1274](https://github.com/tiangolo/full-stack-fastapi-template/pull/1274) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Reuse database url from config in alembic setup. PR [#1229](https://github.com/tiangolo/full-stack-fastapi-template/pull/1229) by [@patrick91](https://github.com/patrick91). -* πŸ”§ Update Playwright config and tests to use env variables. PR [#1266](https://github.com/tiangolo/full-stack-fastapi-template/pull/1266) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Edit refactor db models to use UUID's instead of integer ID's. PR [#1259](https://github.com/tiangolo/full-stack-fastapi-template/pull/1259) by [@estebanx64](https://github.com/estebanx64). -* ♻️ Update form inputs width. PR [#1263](https://github.com/tiangolo/full-stack-fastapi-template/pull/1263) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Replace deprecated utcnow() with now(timezone.utc) in utils module. PR [#1247](https://github.com/tiangolo/full-stack-fastapi-template/pull/1247) by [@jalvarezz13](https://github.com/jalvarezz13). -* 🎨 Format frontend. PR [#1262](https://github.com/tiangolo/full-stack-fastapi-template/pull/1262) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Abstraction of specific AddModal component out of the Navbar. PR [#1246](https://github.com/tiangolo/full-stack-fastapi-template/pull/1246) by [@ajbloureiro](https://github.com/ajbloureiro). -* ♻️ Update `login.tsx` to prevent error if username or password are empty. PR [#1257](https://github.com/tiangolo/full-stack-fastapi-template/pull/1257) by [@jmondaud](https://github.com/jmondaud). -* ♻️ Refactor recover password. PR [#1242](https://github.com/tiangolo/full-stack-fastapi-template/pull/1242) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Format and lint . PR [#1243](https://github.com/tiangolo/full-stack-fastapi-template/pull/1243) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Run biome after OpenAPI client generation. PR [#1226](https://github.com/tiangolo/full-stack-fastapi-template/pull/1226) by [@tomerb](https://github.com/tomerb). -* ♻️ Update DeleteConfirmation component to use new service. PR [#1224](https://github.com/tiangolo/full-stack-fastapi-template/pull/1224) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Update client services. PR [#1223](https://github.com/tiangolo/full-stack-fastapi-template/pull/1223) by [@alejsdev](https://github.com/alejsdev). -* βš’οΈ Add minor frontend tweaks. PR [#1210](https://github.com/tiangolo/full-stack-fastapi-template/pull/1210) by [@alejsdev](https://github.com/alejsdev). -* 🚚 Move assets to public folder. PR [#1206](https://github.com/tiangolo/full-stack-fastapi-template/pull/1206) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor redirect labels to simplify removing the frontend. PR [#1208](https://github.com/tiangolo/full-stack-fastapi-template/pull/1208) by [@tiangolo](https://github.com/tiangolo). -* πŸ”’οΈ Refactor migrate from python-jose to PyJWT. PR [#1203](https://github.com/tiangolo/full-stack-fastapi-template/pull/1203) by [@estebanx64](https://github.com/estebanx64). -* πŸ”₯ Remove duplicated code. PR [#1185](https://github.com/tiangolo/full-stack-fastapi-template/pull/1185) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Add delete_user_me endpoint and corresponding test cases. PR [#1179](https://github.com/tiangolo/full-stack-fastapi-template/pull/1179) by [@alejsdev](https://github.com/alejsdev). -* βœ… Update test to add verification database records. PR [#1178](https://github.com/tiangolo/full-stack-fastapi-template/pull/1178) by [@estebanx64](https://github.com/estebanx64). -* 🚸 Use `useSuspenseQuery` to fetch members and show skeleton. PR [#1174](https://github.com/tiangolo/full-stack-fastapi-template/pull/1174) by [@patrick91](https://github.com/patrick91). -* 🎨 Format Utils. PR [#1173](https://github.com/tiangolo/full-stack-fastapi-template/pull/1173) by [@alejsdev](https://github.com/alejsdev). -* ✨ Use suspense for items page. PR [#1167](https://github.com/tiangolo/full-stack-fastapi-template/pull/1167) by [@patrick91](https://github.com/patrick91). -* 🚸 Mark login field as required. PR [#1166](https://github.com/tiangolo/full-stack-fastapi-template/pull/1166) by [@patrick91](https://github.com/patrick91). -* 🚸 Improve login. PR [#1163](https://github.com/tiangolo/full-stack-fastapi-template/pull/1163) by [@patrick91](https://github.com/patrick91). -* πŸ₯… Handle AxiosErrors in Login page. PR [#1162](https://github.com/tiangolo/full-stack-fastapi-template/pull/1162) by [@patrick91](https://github.com/patrick91). -* 🎨 Format frontend. PR [#1161](https://github.com/tiangolo/full-stack-fastapi-template/pull/1161) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Regenerate frontend client. PR [#1156](https://github.com/tiangolo/full-stack-fastapi-template/pull/1156) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor rename ModelsOut to ModelsPublic. PR [#1154](https://github.com/tiangolo/full-stack-fastapi-template/pull/1154) by [@estebanx64](https://github.com/estebanx64). -* ♻️ Migrate frontend client generation from `openapi-typescript-codegen` to `@hey-api/openapi-ts`. PR [#1151](https://github.com/tiangolo/full-stack-fastapi-template/pull/1151) by [@alejsdev](https://github.com/alejsdev). -* πŸ”₯ Remove unused exports and update dependencies. PR [#1146](https://github.com/tiangolo/full-stack-fastapi-template/pull/1146) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Update sentry dns initialization following the environment settings. PR [#1145](https://github.com/tiangolo/full-stack-fastapi-template/pull/1145) by [@estebanx64](https://github.com/estebanx64). -* ♻️ Refactor and tweaks, rename `UserCreateOpen` to `UserRegister` and others. PR [#1143](https://github.com/tiangolo/full-stack-fastapi-template/pull/1143) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Format imports. PR [#1140](https://github.com/tiangolo/full-stack-fastapi-template/pull/1140) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor and remove `React.FC`. PR [#1139](https://github.com/tiangolo/full-stack-fastapi-template/pull/1139) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Add email pattern and refactor in frontend. PR [#1138](https://github.com/tiangolo/full-stack-fastapi-template/pull/1138) by [@alejsdev](https://github.com/alejsdev). -* πŸ₯… Set up Sentry for FastAPI applications. PR [#1136](https://github.com/tiangolo/full-stack-fastapi-template/pull/1136) by [@estebanx64](https://github.com/estebanx64). -* πŸ”₯ Remove deprecated Docker Compose version key. PR [#1129](https://github.com/tiangolo/full-stack-fastapi-template/pull/1129) by [@tiangolo](https://github.com/tiangolo). -* 🎨 Format with Biome . PR [#1097](https://github.com/tiangolo/full-stack-fastapi-template/pull/1097) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Update quote style in biome formatter. PR [#1095](https://github.com/tiangolo/full-stack-fastapi-template/pull/1095) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Replace ESLint and Prettier with Biome to format and lint frontend. PR [#719](https://github.com/tiangolo/full-stack-fastapi-template/pull/719) by [@santigandolfo](https://github.com/santigandolfo). -* 🎨 Replace buttons styling for variants for consistency. PR [#722](https://github.com/tiangolo/full-stack-fastapi-template/pull/722) by [@alejsdev](https://github.com/alejsdev). -* πŸ› οΈ Improve `modify-openapi-operationids.js`. PR [#720](https://github.com/tiangolo/full-stack-fastapi-template/pull/720) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Replace pytest-mock with unittest.mock and remove pytest-cov. PR [#717](https://github.com/tiangolo/full-stack-fastapi-template/pull/717) by [@estebanx64](https://github.com/estebanx64). -* πŸ› οΈ Minor changes in frontend. PR [#715](https://github.com/tiangolo/full-stack-fastapi-template/pull/715) by [@alejsdev](https://github.com/alejsdev). -* β™» Update Docker image to prevent errors in M1 Macs. PR [#710](https://github.com/tiangolo/full-stack-fastapi-template/pull/710) by [@dudil](https://github.com/dudil). -* ✏ Fix typo in variable names in `backend/app/api/routes/items.py` and `backend/app/api/routes/users.py`. PR [#711](https://github.com/tiangolo/full-stack-fastapi-template/pull/711) by [@disrupted](https://github.com/disrupted). - -### Upgrades - -* ⬆️ Update SQLModel to version `>=0.0.21`. PR [#1275](https://github.com/tiangolo/full-stack-fastapi-template/pull/1275) by [@alejsdev](https://github.com/alejsdev). -* ⬆️ Upgrade Traefik. PR [#1241](https://github.com/tiangolo/full-stack-fastapi-template/pull/1241) by [@tiangolo](https://github.com/tiangolo). -* ⬆️ Bump requests from 2.31.0 to 2.32.0 in /backend. PR [#1211](https://github.com/tiangolo/full-stack-fastapi-template/pull/1211) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆️ Bump jinja2 from 3.1.3 to 3.1.4 in /backend. PR [#1196](https://github.com/tiangolo/full-stack-fastapi-template/pull/1196) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump gunicorn from 21.2.0 to 22.0.0 in /backend. PR [#1176](https://github.com/tiangolo/full-stack-fastapi-template/pull/1176) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump idna from 3.6 to 3.7 in /backend. PR [#1168](https://github.com/tiangolo/full-stack-fastapi-template/pull/1168) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ†™ Update React Query to TanStack Query. PR [#1153](https://github.com/tiangolo/full-stack-fastapi-template/pull/1153) by [@patrick91](https://github.com/patrick91). -* Bump vite from 5.0.12 to 5.0.13 in /frontend. PR [#1149](https://github.com/tiangolo/full-stack-fastapi-template/pull/1149) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump follow-redirects from 1.15.5 to 1.15.6 in /frontend. PR [#734](https://github.com/tiangolo/full-stack-fastapi-template/pull/734) by [@dependabot[bot]](https://github.com/apps/dependabot). - -### Docs - -* πŸ“ Update links from tiangolo repo to fastapi org repo. PR [#1285](https://github.com/fastapi/full-stack-fastapi-template/pull/1285) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Add End-to-End Testing with Playwright to frontend `README.md`. PR [#1279](https://github.com/tiangolo/full-stack-fastapi-template/pull/1279) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update release-notes.md. PR [#1220](https://github.com/tiangolo/full-stack-fastapi-template/pull/1220) by [@alejsdev](https://github.com/alejsdev). -* ✏️ Update `README.md`. PR [#1205](https://github.com/tiangolo/full-stack-fastapi-template/pull/1205) by [@Craz1k0ek](https://github.com/Craz1k0ek). -* ✏️ Fix Adminer URL in `deployment.md`. PR [#1194](https://github.com/tiangolo/full-stack-fastapi-template/pull/1194) by [@PhilippWu](https://github.com/PhilippWu). -* πŸ“ Add `Enabling Open User Registration` to backend docs. PR [#1191](https://github.com/tiangolo/full-stack-fastapi-template/pull/1191) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update release-notes.md. PR [#1164](https://github.com/tiangolo/full-stack-fastapi-template/pull/1164) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update `README.md`. PR [#716](https://github.com/tiangolo/full-stack-fastapi-template/pull/716) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update instructions to clone for a private repo, including updates. PR [#1127](https://github.com/tiangolo/full-stack-fastapi-template/pull/1127) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Add docs about CI keys, LATEST_CHANGES and SMOKESHOW_AUTH_KEY. PR [#1126](https://github.com/tiangolo/full-stack-fastapi-template/pull/1126) by [@tiangolo](https://github.com/tiangolo). -* ✏️ Fix file path in `backend/README.md` when not wanting to use migrations. PR [#1116](https://github.com/tiangolo/full-stack-fastapi-template/pull/1116) by [@leonlowitzki](https://github.com/leonlowitzki). -* πŸ“ Add documentation for pre-commit and code linting. PR [#718](https://github.com/tiangolo/full-stack-fastapi-template/pull/718) by [@estebanx64](https://github.com/estebanx64). -* πŸ“ Fix localhost URLs in `development.md`. PR [#1099](https://github.com/tiangolo/full-stack-fastapi-template/pull/1099) by [@efonte](https://github.com/efonte). -* ✏ Update header titles for consistency. PR [#708](https://github.com/tiangolo/full-stack-fastapi-template/pull/708) by [@codesmith-emmy](https://github.com/codesmith-emmy). -* πŸ“ Update `README.md`, dark mode screenshot position. PR [#706](https://github.com/tiangolo/full-stack-fastapi-template/pull/706) by [@alejsdev](https://github.com/alejsdev). - -### Internal - -* πŸ”§ Update deploy workflows to exclude the main repository. PR [#1284](https://github.com/tiangolo/full-stack-fastapi-template/pull/1284) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Update issue-manager.yml GitHub Action permissions. PR [#1278](https://github.com/tiangolo/full-stack-fastapi-template/pull/1278) by [@tiangolo](https://github.com/tiangolo). -* ⬆️ Bump setuptools from 69.1.1 to 70.0.0 in /backend. PR [#1255](https://github.com/tiangolo/full-stack-fastapi-template/pull/1255) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆️ Bump certifi from 2024.2.2 to 2024.7.4 in /backend. PR [#1250](https://github.com/tiangolo/full-stack-fastapi-template/pull/1250) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆️ Bump urllib3 from 2.2.1 to 2.2.2 in /backend. PR [#1235](https://github.com/tiangolo/full-stack-fastapi-template/pull/1235) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ”§ Ignore `src/routeTree.gen.ts` in biome. PR [#1175](https://github.com/tiangolo/full-stack-fastapi-template/pull/1175) by [@patrick91](https://github.com/patrick91). -* πŸ‘· Update Smokeshow download artifact GitHub Action. PR [#1198](https://github.com/tiangolo/full-stack-fastapi-template/pull/1198) by [@tiangolo](https://github.com/tiangolo). -* πŸ”§ Update Node.js version in `.nvmrc`. PR [#1192](https://github.com/tiangolo/full-stack-fastapi-template/pull/1192) by [@alejsdev](https://github.com/alejsdev). -* πŸ”₯ Remove ESLint and Prettier from pre-commit config. PR [#1096](https://github.com/tiangolo/full-stack-fastapi-template/pull/1096) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Update mypy config to ignore .venv directories. PR [#1155](https://github.com/tiangolo/full-stack-fastapi-template/pull/1155) by [@tiangolo](https://github.com/tiangolo). -* 🚨 Enable `ARG001` to prevent unused arguments. PR [#1152](https://github.com/tiangolo/full-stack-fastapi-template/pull/1152) by [@patrick91](https://github.com/patrick91). -* πŸ”₯ Remove isort configuration, since we use Ruff now. PR [#1144](https://github.com/tiangolo/full-stack-fastapi-template/pull/1144) by [@patrick91](https://github.com/patrick91). -* πŸ”§ Update pre-commit config to exclude generated client folder. PR [#1150](https://github.com/tiangolo/full-stack-fastapi-template/pull/1150) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Change `.nvmrc` format. PR [#1148](https://github.com/tiangolo/full-stack-fastapi-template/pull/1148) by [@patrick91](https://github.com/patrick91). -* 🎨 Ignore alembic from ruff lint and format. PR [#1131](https://github.com/tiangolo/full-stack-fastapi-template/pull/1131) by [@estebanx64](https://github.com/estebanx64). -* πŸ”§ Add GitHub templates for discussions and issues, and security policy. PR [#1105](https://github.com/tiangolo/full-stack-fastapi-template/pull/1105) by [@alejsdev](https://github.com/alejsdev). -* ⬆ Bump dawidd6/action-download-artifact from 3.1.2 to 3.1.4. PR [#1103](https://github.com/tiangolo/full-stack-fastapi-template/pull/1103) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ”§ Add Biome to pre-commit config. PR [#1098](https://github.com/tiangolo/full-stack-fastapi-template/pull/1098) by [@alejsdev](https://github.com/alejsdev). -* πŸ”₯ Delete leftover celery file. PR [#727](https://github.com/tiangolo/full-stack-fastapi-template/pull/727) by [@dr-neptune](https://github.com/dr-neptune). -* βš™οΈ Update pre-commit config with Prettier and ESLint. PR [#714](https://github.com/tiangolo/full-stack-fastapi-template/pull/714) by [@alejsdev](https://github.com/alejsdev). - -## 0.6.0 - -Latest FastAPI, Pydantic, SQLModel πŸš€ - -Brand new frontend with React, TS, Vite, Chakra UI, TanStack Query/Router, generated client/SDK 🎨 - -CI/CD - GitHub Actions πŸ€– - -Test cov > 90% βœ… - -### Features - -* ✨ Adopt SQLModel, create models, start using it. PR [#559](https://github.com/tiangolo/full-stack-fastapi-template/pull/559) by [@tiangolo](https://github.com/tiangolo). -* ✨ Upgrade items router with new SQLModel models, simplified logic, and new FastAPI Annotated dependencies. PR [#560](https://github.com/tiangolo/full-stack-fastapi-template/pull/560) by [@tiangolo](https://github.com/tiangolo). -* ✨ Migrate from pgAdmin to Adminer. PR [#692](https://github.com/tiangolo/full-stack-fastapi-template/pull/692) by [@tiangolo](https://github.com/tiangolo). -* ✨ Add support for setting `POSTGRES_PORT`. PR [#333](https://github.com/tiangolo/full-stack-fastapi-template/pull/333) by [@uepoch](https://github.com/uepoch). -* ⬆ Upgrade Flower version and command. PR [#447](https://github.com/tiangolo/full-stack-fastapi-template/pull/447) by [@maurob](https://github.com/maurob). -* 🎨 Improve styles. PR [#673](https://github.com/tiangolo/full-stack-fastapi-template/pull/673) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Update theme. PR [#666](https://github.com/tiangolo/full-stack-fastapi-template/pull/666) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Add continuous deployment and refactors needed for it. PR [#667](https://github.com/tiangolo/full-stack-fastapi-template/pull/667) by [@tiangolo](https://github.com/tiangolo). -* ✨ Create endpoint to show password recovery email content and update email template. PR [#664](https://github.com/tiangolo/full-stack-fastapi-template/pull/664) by [@alejsdev](https://github.com/alejsdev). -* 🎨 Format with Prettier. PR [#646](https://github.com/tiangolo/full-stack-fastapi-template/pull/646) by [@alejsdev](https://github.com/alejsdev). -* βœ… Add tests to raise coverage to at least 90% and fix recover password logic. PR [#632](https://github.com/tiangolo/full-stack-fastapi-template/pull/632) by [@estebanx64](https://github.com/estebanx64). -* βš™οΈ Add Prettier and ESLint config with pre-commit. PR [#640](https://github.com/tiangolo/full-stack-fastapi-template/pull/640) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Add coverage with Smokeshow to CI and badge. PR [#638](https://github.com/tiangolo/full-stack-fastapi-template/pull/638) by [@estebanx64](https://github.com/estebanx64). -* ✨ Migrate to TanStack Query (React Query) and TanStack Router. PR [#637](https://github.com/tiangolo/full-stack-fastapi-template/pull/637) by [@alejsdev](https://github.com/alejsdev). -* βœ… Add setup and teardown database for tests. PR [#626](https://github.com/tiangolo/full-stack-fastapi-template/pull/626) by [@estebanx64](https://github.com/estebanx64). -* ✨ Update new-frontend client. PR [#625](https://github.com/tiangolo/full-stack-fastapi-template/pull/625) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add password reset functionality. PR [#624](https://github.com/tiangolo/full-stack-fastapi-template/pull/624) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add private/public routing. PR [#621](https://github.com/tiangolo/full-stack-fastapi-template/pull/621) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Add VS Code debug configs. PR [#620](https://github.com/tiangolo/full-stack-fastapi-template/pull/620) by [@tiangolo](https://github.com/tiangolo). -* ✨ Add `Not Found` page. PR [#595](https://github.com/tiangolo/full-stack-fastapi-template/pull/595) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add new pages, components, panels, modals, and theme; refactor and improvements in existing components. PR [#593](https://github.com/tiangolo/full-stack-fastapi-template/pull/593) by [@alejsdev](https://github.com/alejsdev). -* ✨ Support delete own account and other tweaks. PR [#614](https://github.com/tiangolo/full-stack-fastapi-template/pull/614) by [@alejsdev](https://github.com/alejsdev). -* ✨ Restructure folders, allow editing of users/items, and implement other refactors and improvements. PR [#603](https://github.com/tiangolo/full-stack-fastapi-template/pull/603) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Copier, migrate from Cookiecutter, in a way that supports using the project as is, forking or cloning it. PR [#612](https://github.com/tiangolo/full-stack-fastapi-template/pull/612) by [@tiangolo](https://github.com/tiangolo). -* βž• Replace black, isort, flake8, autoflake with ruff and upgrade mypy. PR [#610](https://github.com/tiangolo/full-stack-fastapi-template/pull/610) by [@tiangolo](https://github.com/tiangolo). -* β™» Refactor items and services endpoints to return count and data, and add CI tests. PR [#599](https://github.com/tiangolo/full-stack-fastapi-template/pull/599) by [@estebanx64](https://github.com/estebanx64). -* ✨ Add support for updating items and upgrade SQLModel to 0.0.16 (which supports model object updates). PR [#601](https://github.com/tiangolo/full-stack-fastapi-template/pull/601) by [@tiangolo](https://github.com/tiangolo). -* ✨ Add dark mode to new-frontend and conditional sidebar items. PR [#600](https://github.com/tiangolo/full-stack-fastapi-template/pull/600) by [@alejsdev](https://github.com/alejsdev). -* ✨ Migrate to RouterProvider and other refactors . PR [#598](https://github.com/tiangolo/full-stack-fastapi-template/pull/598) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add delete_user; refactor delete_item. PR [#594](https://github.com/tiangolo/full-stack-fastapi-template/pull/594) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add state store to new frontend. PR [#592](https://github.com/tiangolo/full-stack-fastapi-template/pull/592) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add form validation to Admin, Items and Login. PR [#616](https://github.com/tiangolo/full-stack-fastapi-template/pull/616) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Sidebar to new frontend. PR [#587](https://github.com/tiangolo/full-stack-fastapi-template/pull/587) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Login to new frontend. PR [#585](https://github.com/tiangolo/full-stack-fastapi-template/pull/585) by [@alejsdev](https://github.com/alejsdev). -* ✨ Include schemas in generated frontend client. PR [#584](https://github.com/tiangolo/full-stack-fastapi-template/pull/584) by [@alejsdev](https://github.com/alejsdev). -* ✨ Regenerate frontend client with recent changes. PR [#575](https://github.com/tiangolo/full-stack-fastapi-template/pull/575) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor API in `utils.py`. PR [#573](https://github.com/tiangolo/full-stack-fastapi-template/pull/573) by [@alejsdev](https://github.com/alejsdev). -* ✨ Update code for login API. PR [#571](https://github.com/tiangolo/full-stack-fastapi-template/pull/571) by [@tiangolo](https://github.com/tiangolo). -* ✨ Add client in frontend and client generation. PR [#569](https://github.com/tiangolo/full-stack-fastapi-template/pull/569) by [@alejsdev](https://github.com/alejsdev). -* 🐳 Set up Docker config for new-frontend. PR [#564](https://github.com/tiangolo/full-stack-fastapi-template/pull/564) by [@alejsdev](https://github.com/alejsdev). -* ✨ Set up new frontend with Vite, TypeScript and React. PR [#563](https://github.com/tiangolo/full-stack-fastapi-template/pull/563) by [@alejsdev](https://github.com/alejsdev). -* πŸ“Œ Add NodeJS version management and instructions. PR [#551](https://github.com/tiangolo/full-stack-fastapi-template/pull/551) by [@alejsdev](https://github.com/alejsdev). -* Add consistent errors for env vars not set. PR [#200](https://github.com/tiangolo/full-stack-fastapi-template/pull/200). -* Upgrade Traefik to version 2, keeping in sync with DockerSwarm.rocks. PR [#199](https://github.com/tiangolo/full-stack-fastapi-template/pull/199). -* Run tests with `TestClient`. PR [#160](https://github.com/tiangolo/full-stack-fastapi-template/pull/160). - -### Fixes - -* πŸ› Fix copier to handle string vars with spaces in quotes. PR [#631](https://github.com/tiangolo/full-stack-fastapi-template/pull/631) by [@estebanx64](https://github.com/estebanx64). -* πŸ› Fix allowing a user to update the email to the same email they already have. PR [#696](https://github.com/tiangolo/full-stack-fastapi-template/pull/696) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Set up Sentry only when used. PR [#671](https://github.com/tiangolo/full-stack-fastapi-template/pull/671) by [@tiangolo](https://github.com/tiangolo). -* πŸ”₯ Remove unnecessary validation. PR [#662](https://github.com/tiangolo/full-stack-fastapi-template/pull/662) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Fix bug when editing own user. PR [#651](https://github.com/tiangolo/full-stack-fastapi-template/pull/651) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Add `onClose` to `SidebarItems`. PR [#589](https://github.com/tiangolo/full-stack-fastapi-template/pull/589) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Fix positional argument bug in `init_db.py`. PR [#562](https://github.com/tiangolo/full-stack-fastapi-template/pull/562) by [@alejsdev](https://github.com/alejsdev). -* πŸ“Œ Fix flower Docker image, pin version. PR [#396](https://github.com/tiangolo/full-stack-fastapi-template/pull/396) by [@sanggusti](https://github.com/sanggusti). -* πŸ› Fix Celery worker command. PR [#443](https://github.com/tiangolo/full-stack-fastapi-template/pull/443) by [@bechtold](https://github.com/bechtold). -* πŸ› Fix Poetry installation in Dockerfile and upgrade Python version and packages to fix Docker build. PR [#480](https://github.com/tiangolo/full-stack-fastapi-template/pull/480) by [@little7Li](https://github.com/little7Li). - -### Refactors - -* πŸ”§ Add missing dotenv variables. PR [#554](https://github.com/tiangolo/full-stack-fastapi-template/pull/554) by [@tiangolo](https://github.com/tiangolo). -* βͺ Revert "βš™οΈ Add Prettier and ESLint config with pre-commit". PR [#644](https://github.com/tiangolo/full-stack-fastapi-template/pull/644) by [@alejsdev](https://github.com/alejsdev). -* πŸ™ˆ Add .prettierignore and include client folder. PR [#648](https://github.com/tiangolo/full-stack-fastapi-template/pull/648) by [@alejsdev](https://github.com/alejsdev). -* 🏷️ Add mypy to the GitHub Action for tests and fixed types in the whole project. PR [#655](https://github.com/tiangolo/full-stack-fastapi-template/pull/655) by [@estebanx64](https://github.com/estebanx64). -* πŸ”’οΈ Ensure the default values of "changethis" are not deployed. PR [#698](https://github.com/tiangolo/full-stack-fastapi-template/pull/698) by [@tiangolo](https://github.com/tiangolo). -* β—€ Revert "πŸ“Έ Rename Dashboard to Home and update screenshots". PR [#697](https://github.com/tiangolo/full-stack-fastapi-template/pull/697) by [@alejsdev](https://github.com/alejsdev). -* πŸ“Έ Rename Dashboard to Home and update screenshots. PR [#693](https://github.com/tiangolo/full-stack-fastapi-template/pull/693) by [@alejsdev](https://github.com/alejsdev). -* πŸ› Fixed items count when retrieving data for all items by user. PR [#695](https://github.com/tiangolo/full-stack-fastapi-template/pull/695) by [@estebanx64](https://github.com/estebanx64). -* πŸ”₯ Remove Celery and Flower, they are currently not used nor recommended. PR [#694](https://github.com/tiangolo/full-stack-fastapi-template/pull/694) by [@tiangolo](https://github.com/tiangolo). -* βœ… Add test for deleting user without privileges. PR [#690](https://github.com/tiangolo/full-stack-fastapi-template/pull/690) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor user update. PR [#689](https://github.com/tiangolo/full-stack-fastapi-template/pull/689) by [@alejsdev](https://github.com/alejsdev). -* πŸ“Œ Add Poetry lock to git. PR [#685](https://github.com/tiangolo/full-stack-fastapi-template/pull/685) by [@tiangolo](https://github.com/tiangolo). -* 🎨 Adjust color and spacing. PR [#684](https://github.com/tiangolo/full-stack-fastapi-template/pull/684) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Avoid creating unnecessary *.pyc files with PYTHONDONTWRITEBYTECODE=1. PR [#677](https://github.com/tiangolo/full-stack-fastapi-template/pull/677) by [@estebanx64](https://github.com/estebanx64). -* πŸ”§ Add `SMTP_SSL` option for older SMTP servers. PR [#365](https://github.com/tiangolo/full-stack-fastapi-template/pull/365) by [@Metrea](https://github.com/Metrea). -* ♻️ Refactor logic to allow running pytest tests locally. PR [#683](https://github.com/tiangolo/full-stack-fastapi-template/pull/683) by [@tiangolo](https://github.com/tiangolo). -* β™» Update error messages. PR [#417](https://github.com/tiangolo/full-stack-fastapi-template/pull/417) by [@qu3vipon](https://github.com/qu3vipon). -* πŸ”§ Add a default Flower password. PR [#682](https://github.com/tiangolo/full-stack-fastapi-template/pull/682) by [@tiangolo](https://github.com/tiangolo). -* πŸ”§ Update VS Code debug config. PR [#676](https://github.com/tiangolo/full-stack-fastapi-template/pull/676) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Refactor code structure for tests. PR [#674](https://github.com/tiangolo/full-stack-fastapi-template/pull/674) by [@tiangolo](https://github.com/tiangolo). -* πŸ”§ Set TanStack Router devtools only in dev mode. PR [#668](https://github.com/tiangolo/full-stack-fastapi-template/pull/668) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor email logic to allow re-using util functions for testing and development. PR [#663](https://github.com/tiangolo/full-stack-fastapi-template/pull/663) by [@tiangolo](https://github.com/tiangolo). -* πŸ’¬ Improve Delete Account description and confirmation. PR [#661](https://github.com/tiangolo/full-stack-fastapi-template/pull/661) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor email templates. PR [#659](https://github.com/tiangolo/full-stack-fastapi-template/pull/659) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update deployment files and docs. PR [#660](https://github.com/tiangolo/full-stack-fastapi-template/pull/660) by [@tiangolo](https://github.com/tiangolo). -* πŸ”₯ Remove unused schemas. PR [#656](https://github.com/tiangolo/full-stack-fastapi-template/pull/656) by [@alejsdev](https://github.com/alejsdev). -* πŸ”₯ Remove old frontend. PR [#649](https://github.com/tiangolo/full-stack-fastapi-template/pull/649) by [@tiangolo](https://github.com/tiangolo). -* β™» Move project source files to top level from src, update Sentry dependency. PR [#630](https://github.com/tiangolo/full-stack-fastapi-template/pull/630) by [@estebanx64](https://github.com/estebanx64). -* β™» Refactor Python folder tree. PR [#629](https://github.com/tiangolo/full-stack-fastapi-template/pull/629) by [@estebanx64](https://github.com/estebanx64). -* ♻️ Refactor old CRUD utils and tests. PR [#622](https://github.com/tiangolo/full-stack-fastapi-template/pull/622) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Update .env to allow local debug for the backend. PR [#618](https://github.com/tiangolo/full-stack-fastapi-template/pull/618) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Refactor and update CORS, remove trailing slash from new Pydantic v2. PR [#617](https://github.com/tiangolo/full-stack-fastapi-template/pull/617) by [@tiangolo](https://github.com/tiangolo). -* 🎨 Format files with pre-commit and Ruff. PR [#611](https://github.com/tiangolo/full-stack-fastapi-template/pull/611) by [@tiangolo](https://github.com/tiangolo). -* 🚚 Refactor and simplify backend file structure. PR [#609](https://github.com/tiangolo/full-stack-fastapi-template/pull/609) by [@tiangolo](https://github.com/tiangolo). -* πŸ”₯ Clean up old files no longer relevant. PR [#608](https://github.com/tiangolo/full-stack-fastapi-template/pull/608) by [@tiangolo](https://github.com/tiangolo). -* β™» Re-structure Docker Compose files, discard Docker Swarm specific logic. PR [#607](https://github.com/tiangolo/full-stack-fastapi-template/pull/607) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Refactor update endpoints and regenerate client for new-frontend. PR [#602](https://github.com/tiangolo/full-stack-fastapi-template/pull/602) by [@alejsdev](https://github.com/alejsdev). -* ✨ Add Layout to App. PR [#588](https://github.com/tiangolo/full-stack-fastapi-template/pull/588) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Re-enable user update path operations for frontend client generation. PR [#574](https://github.com/tiangolo/full-stack-fastapi-template/pull/574) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Remove type ignores and add `response_model`. PR [#572](https://github.com/tiangolo/full-stack-fastapi-template/pull/572) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor Users API and dependencies. PR [#561](https://github.com/tiangolo/full-stack-fastapi-template/pull/561) by [@alejsdev](https://github.com/alejsdev). -* ♻️ Refactor frontend Docker build setup, use plain NodeJS, use custom Nginx config, fix build for old Vue. PR [#555](https://github.com/tiangolo/full-stack-fastapi-template/pull/555) by [@tiangolo](https://github.com/tiangolo). -* ♻️ Refactor project generation, discard cookiecutter, use plain git/clone/fork. PR [#553](https://github.com/tiangolo/full-stack-fastapi-template/pull/553) by [@tiangolo](https://github.com/tiangolo). -* Refactor backend: - * Simplify configs for tools and format to better support editor integration. - * Add mypy configurations and plugins. - * Add types to all the codebase. - * Update types for SQLAlchemy models with plugin. - * Update and refactor CRUD utils. - * Refactor DB sessions to use dependencies with `yield`. - * Refactor dependencies, security, CRUD, models, schemas, etc. To simplify code and improve autocompletion. - * Change from PyJWT to Python-JOSE as it supports additional use cases. - * Fix JWT tokens using user email/ID as the subject in `sub`. - * PR [#158](https://github.com/tiangolo/full-stack-fastapi-template/pull/158). -* Simplify `docker-compose.*.yml` files, refactor deployment to reduce config files. PR [#153](https://github.com/tiangolo/full-stack-fastapi-template/pull/153). -* Simplify env var files, merge to a single `.env` file. PR [#151](https://github.com/tiangolo/full-stack-fastapi-template/pull/151). - -### Upgrades - -* πŸ“Œ Upgrade Poetry lock dependencies. PR [#702](https://github.com/tiangolo/full-stack-fastapi-template/pull/702) by [@tiangolo](https://github.com/tiangolo). -* ⬆️ Upgrade Python version and dependencies. PR [#558](https://github.com/tiangolo/full-stack-fastapi-template/pull/558) by [@tiangolo](https://github.com/tiangolo). -* ⬆ Bump tiangolo/issue-manager from 0.2.0 to 0.5.0. PR [#591](https://github.com/tiangolo/full-stack-fastapi-template/pull/591) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump follow-redirects from 1.15.3 to 1.15.5 in /frontend. PR [#654](https://github.com/tiangolo/full-stack-fastapi-template/pull/654) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump vite from 5.0.4 to 5.0.12 in /frontend. PR [#653](https://github.com/tiangolo/full-stack-fastapi-template/pull/653) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump fastapi from 0.104.1 to 0.109.1 in /backend. PR [#687](https://github.com/tiangolo/full-stack-fastapi-template/pull/687) by [@dependabot[bot]](https://github.com/apps/dependabot). -* Bump python-multipart from 0.0.6 to 0.0.7 in /backend. PR [#686](https://github.com/tiangolo/full-stack-fastapi-template/pull/686) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Add `uvicorn[standard]` to include `watchgod` and `uvloop`. PR [#438](https://github.com/tiangolo/full-stack-fastapi-template/pull/438) by [@alonme](https://github.com/alonme). -* ⬆ Upgrade code to support pydantic V2. PR [#615](https://github.com/tiangolo/full-stack-fastapi-template/pull/615) by [@estebanx64](https://github.com/estebanx64). - -### Docs - -* πŸ¦‡ Add dark mode to `README.md`. PR [#703](https://github.com/tiangolo/full-stack-fastapi-template/pull/703) by [@alejsdev](https://github.com/alejsdev). -* 🍱 Update GitHub image. PR [#701](https://github.com/tiangolo/full-stack-fastapi-template/pull/701) by [@tiangolo](https://github.com/tiangolo). -* 🍱 Add GitHub image. PR [#700](https://github.com/tiangolo/full-stack-fastapi-template/pull/700) by [@tiangolo](https://github.com/tiangolo). -* 🚚 Rename project to Full Stack FastAPI Template. PR [#699](https://github.com/tiangolo/full-stack-fastapi-template/pull/699) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Update `README.md`. PR [#691](https://github.com/tiangolo/full-stack-fastapi-template/pull/691) by [@alejsdev](https://github.com/alejsdev). -* ✏ Fix typo in `development.md`. PR [#309](https://github.com/tiangolo/full-stack-fastapi-template/pull/309) by [@graue70](https://github.com/graue70). -* πŸ“ Add docs for wildcard domains. PR [#681](https://github.com/tiangolo/full-stack-fastapi-template/pull/681) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Add the required GitHub Actions secrets to docs. PR [#679](https://github.com/tiangolo/full-stack-fastapi-template/pull/679) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Update `README.md` and `deployment.md`. PR [#678](https://github.com/tiangolo/full-stack-fastapi-template/pull/678) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update frontend `README.md`. PR [#675](https://github.com/tiangolo/full-stack-fastapi-template/pull/675) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Update deployment docs to use a different directory for traefik-public. PR [#670](https://github.com/tiangolo/full-stack-fastapi-template/pull/670) by [@tiangolo](https://github.com/tiangolo). -* πŸ“Έ Add new screenshots . PR [#657](https://github.com/tiangolo/full-stack-fastapi-template/pull/657) by [@alejsdev](https://github.com/alejsdev). -* πŸ“ Refactor README into separate README.md files for backend, frontend, deployment, development. PR [#639](https://github.com/tiangolo/full-stack-fastapi-template/pull/639) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Update README. PR [#628](https://github.com/tiangolo/full-stack-fastapi-template/pull/628) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Update GitHub Action latest-changes and move release notes to independent file. PR [#619](https://github.com/tiangolo/full-stack-fastapi-template/pull/619) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Update internal README and referred files. PR [#613](https://github.com/tiangolo/full-stack-fastapi-template/pull/613) by [@tiangolo](https://github.com/tiangolo). -* πŸ“ Update README with in construction notice. PR [#552](https://github.com/tiangolo/full-stack-fastapi-template/pull/552) by [@tiangolo](https://github.com/tiangolo). -* Add docs about reporting test coverage in HTML. PR [#161](https://github.com/tiangolo/full-stack-fastapi-template/pull/161). -* Add docs about removing the frontend, for an API-only app. PR [#156](https://github.com/tiangolo/full-stack-fastapi-template/pull/156). - -### Internal - -* πŸ‘· Add Lint to GitHub Actions outside of tests. PR [#688](https://github.com/tiangolo/full-stack-fastapi-template/pull/688) by [@tiangolo](https://github.com/tiangolo). -* ⬆ Bump dawidd6/action-download-artifact from 2.28.0 to 3.1.2. PR [#643](https://github.com/tiangolo/full-stack-fastapi-template/pull/643) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Bump actions/upload-artifact from 3 to 4. PR [#642](https://github.com/tiangolo/full-stack-fastapi-template/pull/642) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ⬆ Bump actions/setup-python from 4 to 5. PR [#641](https://github.com/tiangolo/full-stack-fastapi-template/pull/641) by [@dependabot[bot]](https://github.com/apps/dependabot). -* πŸ‘· Tweak test GitHub Action names. PR [#672](https://github.com/tiangolo/full-stack-fastapi-template/pull/672) by [@tiangolo](https://github.com/tiangolo). -* πŸ”§ Add `.gitattributes` file to ensure LF endings for `.sh` files. PR [#658](https://github.com/tiangolo/full-stack-fastapi-template/pull/658) by [@estebanx64](https://github.com/estebanx64). -* 🚚 Move new-frontend to frontend. PR [#652](https://github.com/tiangolo/full-stack-fastapi-template/pull/652) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Add script for ESLint. PR [#650](https://github.com/tiangolo/full-stack-fastapi-template/pull/650) by [@alejsdev](https://github.com/alejsdev). -* βš™οΈ Add Prettier config. PR [#647](https://github.com/tiangolo/full-stack-fastapi-template/pull/647) by [@alejsdev](https://github.com/alejsdev). -* πŸ”§ Update pre-commit config. PR [#645](https://github.com/tiangolo/full-stack-fastapi-template/pull/645) by [@alejsdev](https://github.com/alejsdev). -* πŸ‘· Add dependabot. PR [#547](https://github.com/tiangolo/full-stack-fastapi-template/pull/547) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Fix latest-changes GitHub Action token, strike 2. PR [#546](https://github.com/tiangolo/full-stack-fastapi-template/pull/546) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Fix latest-changes GitHub Action token config. PR [#545](https://github.com/tiangolo/full-stack-fastapi-template/pull/545) by [@tiangolo](https://github.com/tiangolo). -* πŸ‘· Add latest-changes GitHub Action. PR [#544](https://github.com/tiangolo/full-stack-fastapi-template/pull/544) by [@tiangolo](https://github.com/tiangolo). -* Update issue-manager. PR [#211](https://github.com/tiangolo/full-stack-fastapi-template/pull/211). -* Add [GitHub Sponsors](https://github.com/sponsors/tiangolo) button. PR [#201](https://github.com/tiangolo/full-stack-fastapi-template/pull/201). -* Simplify scripts and development, update docs and configs. PR [#155](https://github.com/tiangolo/full-stack-fastapi-template/pull/155). - -## 0.5.0 - -* Make the Traefik public network a fixed default of `traefik-public` as done in DockerSwarm.rocks, to simplify development and iteration of the project generator. PR [#150](https://github.com/tiangolo/full-stack-fastapi-template/pull/150). -* Update to PostgreSQL 12. PR [#148](https://github.com/tiangolo/full-stack-fastapi-template/pull/148). by [@RCheese](https://github.com/RCheese). -* Use Poetry for package management. Initial PR [#144](https://github.com/tiangolo/full-stack-fastapi-template/pull/144) by [@RCheese](https://github.com/RCheese). -* Fix Windows line endings for shell scripts after project generation with Cookiecutter hooks. PR [#149](https://github.com/tiangolo/full-stack-fastapi-template/pull/149). -* Upgrade Vue CLI to version 4. PR [#120](https://github.com/tiangolo/full-stack-fastapi-template/pull/120) by [@br3ndonland](https://github.com/br3ndonland). -* Remove duplicate `login` tag. PR [#135](https://github.com/tiangolo/full-stack-fastapi-template/pull/135) by [@Nonameentered](https://github.com/Nonameentered). -* Fix showing email in dashboard when there's no user's full name. PR [#129](https://github.com/tiangolo/full-stack-fastapi-template/pull/129) by [@rlonka](https://github.com/rlonka). -* Format code with Black and Flake8. PR [#121](https://github.com/tiangolo/full-stack-fastapi-template/pull/121) by [@br3ndonland](https://github.com/br3ndonland). -* Simplify SQLAlchemy Base class. PR [#117](https://github.com/tiangolo/full-stack-fastapi-template/pull/117) by [@airibarne](https://github.com/airibarne). -* Update CRUD utils for users, handling password hashing. PR [#106](https://github.com/tiangolo/full-stack-fastapi-template/pull/106) by [@mocsar](https://github.com/mocsar). -* Use `.` instead of `source` for interoperability. PR [#98](https://github.com/tiangolo/full-stack-fastapi-template/pull/98) by [@gucharbon](https://github.com/gucharbon). -* Use Pydantic's `BaseSettings` for settings/configs and env vars. PR [#87](https://github.com/tiangolo/full-stack-fastapi-template/pull/87) by [@StephenBrown2](https://github.com/StephenBrown2). -* Remove `package-lock.json` to let everyone lock their own versions (depending on OS, etc). -* Simplify Traefik service labels PR [#139](https://github.com/tiangolo/full-stack-fastapi-template/pull/139). -* Add email validation. PR [#40](https://github.com/tiangolo/full-stack-fastapi-template/pull/40) by [@kedod](https://github.com/kedod). -* Fix typo in README. PR [#83](https://github.com/tiangolo/full-stack-fastapi-template/pull/83) by [@ashears](https://github.com/ashears). -* Fix typo in README. PR [#80](https://github.com/tiangolo/full-stack-fastapi-template/pull/80) by [@abjoker](https://github.com/abjoker). -* Fix function name `read_item` and response code. PR [#74](https://github.com/tiangolo/full-stack-fastapi-template/pull/74) by [@jcaguirre89](https://github.com/jcaguirre89). -* Fix typo in comment. PR [#70](https://github.com/tiangolo/full-stack-fastapi-template/pull/70) by [@daniel-butler](https://github.com/daniel-butler). -* Fix Flower Docker configuration. PR [#37](https://github.com/tiangolo/full-stack-fastapi-template/pull/37) by [@dmontagu](https://github.com/dmontagu). -* Add new CRUD utils based on DB and Pydantic models. Initial PR [#23](https://github.com/tiangolo/full-stack-fastapi-template/pull/23) by [@ebreton](https://github.com/ebreton). -* Add normal user testing Pytest fixture. PR [#20](https://github.com/tiangolo/full-stack-fastapi-template/pull/20) by [@ebreton](https://github.com/ebreton). - -## 0.4.0 - -* Fix security on resetting a password. Receive token as body, not query. PR [#34](https://github.com/tiangolo/full-stack-fastapi-template/pull/34). - -* Fix security on resetting a password. Receive it as body, not query. PR [#33](https://github.com/tiangolo/full-stack-fastapi-template/pull/33) by [@dmontagu](https://github.com/dmontagu). - -* Fix SQLAlchemy class lookup on initialization. PR [#29](https://github.com/tiangolo/full-stack-fastapi-template/pull/29) by [@ebreton](https://github.com/ebreton). - -* Fix SQLAlchemy operation errors on database restart. PR [#32](https://github.com/tiangolo/full-stack-fastapi-template/pull/32) by [@ebreton](https://github.com/ebreton). - -* Fix locations of scripts in generated README. PR [#19](https://github.com/tiangolo/full-stack-fastapi-template/pull/19) by [@ebreton](https://github.com/ebreton). - -* Forward arguments from script to `pytest` inside container. PR [#17](https://github.com/tiangolo/full-stack-fastapi-template/pull/17) by [@ebreton](https://github.com/ebreton). - -* Update development scripts. - -* Read Alembic configs from env vars. PR #9 by @ebreton. - -* Create DB Item objects from all Pydantic model's fields. - -* Update Jupyter Lab installation and util script/environment variable for local development. - -## 0.3.0 - -* PR #14: - * Update CRUD utils to use types better. - * Simplify Pydantic model names, from `UserInCreate` to `UserCreate`, etc. - * Upgrade packages. - * Add new generic "Items" models, crud utils, endpoints, and tests. To facilitate re-using them to create new functionality. As they are simple and generic (not like Users), it's easier to copy-paste and adapt them to each use case. - * Update endpoints/*path operations* to simplify code and use new utilities, prefix and tags in `include_router`. - * Update testing utils. - * Update linting rules, relax vulture to reduce false positives. - * Update migrations to include new Items. - * Update project README.md with tips about how to start with backend. - -* Upgrade Python to 3.7 as Celery is now compatible too. PR #10 by @ebreton. - -## 0.2.2 - -* Fix frontend hijacking /docs in development. Using latest https://github.com/tiangolo/node-frontend with custom Nginx configs in frontend. PR #6. - -## 0.2.1 - -* Fix documentation for *path operation* to get user by ID. PR #4 by @mpclarkson in FastAPI. - -* Set `/start-reload.sh` as a command override for development by default. - -* Update generated README. - -## 0.2.0 - -**PR #2**: - -* Simplify and update backend `Dockerfile`s. -* Refactor and simplify backend code, improve naming, imports, modules and "namespaces". -* Improve and simplify Vuex integration with TypeScript accessors. -* Standardize frontend components layout, buttons order, etc. -* Add local development scripts (to develop this project generator itself). -* Add logs to startup modules to detect errors early. -* Improve FastAPI dependency utilities, to simplify and reduce code (to require a superuser). - -## 0.1.2 - -* Fix path operation to update self-user, set parameters as body payload. - -## 0.1.1 - -Several bug fixes since initial publication, including: - -* Order of path operations for users. -* Frontend sending login data in the correct format. -* Add https://localhost variants to CORS. diff --git a/scripts/build-push.sh b/scripts/build-push.sh index 3fa3aa7e6b..bcf1eaf27a 100644 --- a/scripts/build-push.sh +++ b/scripts/build-push.sh @@ -1,10 +1,20 @@ -#! /usr/bin/env sh +#!/bin/bash -# Exit in case of error -set -e +# Set your Docker Hub username +DOCKER_USERNAME="dperezsa" +VERSION="1.0.0" -TAG=${TAG?Variable not set} \ -FRONTEND_ENV=${FRONTEND_ENV-production} \ -sh ./scripts/build.sh +# Build backend image +echo "Building backend image..." +docker build -t ${DOCKER_USERNAME}/sdp-backend:${VERSION} -f backend/Dockerfile backend/ -docker-compose -f docker-compose.yml push +# Build frontend image +echo "Building frontend image..." +docker build -t ${DOCKER_USERNAME}/sdp-frontend:${VERSION} -f frontend/Dockerfile frontend/ + +# Push images to Docker Hub +echo "Pushing images to Docker Hub..." +docker push ${DOCKER_USERNAME}/sdp-backend:${VERSION} +docker push ${DOCKER_USERNAME}/sdp-frontend:${VERSION} + +echo "Done!" \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 99faa96bf7..f86feae2b0 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,15 +1,16 @@ -#! /usr/bin/env sh +#!/usr/bin/env sh # Exit in case of error set -e -DOMAIN=${DOMAIN?Variable not set} \ -STACK_NAME=${STACK_NAME?Variable not set} \ -TAG=${TAG?Variable not set} \ -docker-compose \ --f docker-compose.yml \ -config > docker-stack.yml +# Define variables +HELM_CHART_DIR="./kubernetes/charts/sdp" +RELEASE_NAME="sdp" +BACKEND_PORT=8000 +FRONTEND_PORT=5173 +ADMINER_PORT=5173 +TIMEOUT=300 # 5 minute timeout -docker-auto-labels docker-stack.yml - -docker stack deploy -c docker-stack.yml --with-registry-auth "${STACK_NAME?Variable not set}" +# Main deployment +echo "Deploying Helm chart..." +helm upgrade --install $RELEASE_NAME $HELM_CHART_DIR diff --git a/scripts/forward.sh b/scripts/forward.sh new file mode 100644 index 0000000000..e8f8649d76 --- /dev/null +++ b/scripts/forward.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env sh + +# Exit on error +set -e + +# Config +HELM_CHART_DIR="./kubernetes/charts/sdp" +RELEASE_NAME="SDP" +BACKEND_PORT=8000 +FRONTEND_PORT=5173 +ADMINER_PORT=8080 + +# Get pod names using go-template +echo "πŸ” Getting pod names..." +PODS=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}') + +# Identify backend, frontend, and adminer pods +BACKEND_POD="" +FRONTEND_POD="" +ADMINER_POD="" + +for pod in $PODS; do + if echo "$pod" | grep -q "backend"; then + BACKEND_POD="$pod" + elif echo "$pod" | grep -q "frontend"; then + FRONTEND_POD="$pod" + elif echo "$pod" | grep -q "adminer"; then + ADMINER_POD="$pod" + fi +done + +# Report found pods +if [ -z "$BACKEND_POD" ]; then + echo "⚠️ Could not find backend pod, skipping..." +else + echo "βœ… Backend pod: $BACKEND_POD" +fi + +if [ -z "$FRONTEND_POD" ]; then + echo "⚠️ Could not find frontend pod, skipping..." +else + echo "βœ… Frontend pod: $FRONTEND_POD" +fi + +if [ -z "$ADMINER_POD" ]; then + echo "⚠️ Could not find adminer pod, skipping..." +else + echo "βœ… Adminer pod: $ADMINER_POD" +fi + +# Start port forwarding for available pods +echo "πŸ”Œ Starting port forwarding..." + +if [ -n "$BACKEND_POD" ]; then + echo "🌐 Backend API: http://localhost:$BACKEND_PORT" + kubectl port-forward pod/$BACKEND_POD $BACKEND_PORT:8000 & + BACKEND_PID=$! +else + BACKEND_PID="" +fi + +if [ -n "$FRONTEND_POD" ]; then + echo "πŸ–₯️ Frontend: http://localhost:$FRONTEND_PORT" + kubectl port-forward pod/$FRONTEND_POD $FRONTEND_PORT:80 & + FRONTEND_PID=$! +else + FRONTEND_PID="" +fi + +if [ -n "$ADMINER_POD" ]; then + echo "πŸ› οΈ Adminer: http://localhost:$ADMINER_PORT" + kubectl port-forward pod/$ADMINER_POD $ADMINER_PORT:80 & + ADMINER_PID=$! +else + ADMINER_PID="" +fi + +# Cleanup on exit +cleanup() { + echo "πŸ›‘ Stopping port forwarding..." + # Only kill PIDs that exist + [ -n "$BACKEND_PID" ] && kill $BACKEND_PID 2>/dev/null || true + [ -n "$FRONTEND_PID" ] && kill $FRONTEND_PID 2>/dev/null || true + [ -n "$ADMINER_PID" ] && kill $ADMINER_PID 2>/dev/null || true + exit 0 +} +trap cleanup INT + +# Keep running +echo "🎯 Press Ctrl+C to stop" +wait \ No newline at end of file