diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml new file mode 100644 index 00000000..af6abf46 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/questions.yml @@ -0,0 +1,118 @@ +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 new file mode 100644 index 00000000..0ffc101a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [tiangolo] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..50bde360 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000..6438848c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/privileged.yml @@ -0,0 +1,22 @@ +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 index c211782a..e83b2478 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,49 +4,28 @@ updates: - package-ecosystem: github-actions directory: / schedule: - interval: weekly - cooldown: - default-days: 7 + interval: daily commit-message: prefix: โฌ† - labels: - - "internal" - - "dependencies" - - "github_actions" - groups: - github-actions: - patterns: - - "*" + labels: [dependencies, internal] # Python uv - package-ecosystem: uv directory: / schedule: interval: weekly - cooldown: - default-days: 7 commit-message: prefix: โฌ† labels: [dependencies, internal] - groups: - python-packages: - patterns: - - "*" # bun - package-ecosystem: bun directory: / schedule: interval: weekly - cooldown: - default-days: 7 commit-message: prefix: โฌ† labels: [dependencies, internal] ignore: - dependency-name: "@hey-api/openapi-ts" - groups: - npm-packages: - patterns: - - "*" # Docker - package-ecosystem: docker directories: @@ -54,41 +33,14 @@ updates: - /frontend schedule: interval: weekly - cooldown: - default-days: 7 commit-message: prefix: โฌ† - groups: - docker: - patterns: - - "*" + labels: [dependencies, internal] # Docker Compose - package-ecosystem: docker-compose directory: / schedule: interval: weekly - cooldown: - default-days: 7 commit-message: prefix: โฌ† - groups: - docker-compose: - patterns: - - "*" - # pre-commit - - package-ecosystem: "pre-commit" - directory: "/" - schedule: - interval: "weekly" - cooldown: - default-days: 7 - commit-message: - prefix: โฌ† - labels: - - "internal" - - "dependencies" - - "pre-commit" - groups: - pre-commit: - patterns: - - "*" + labels: [dependencies, internal] diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml index 35d08986..0308d7a0 100644 --- a/.github/workflows/add-to-project.yml +++ b/.github/workflows/add-to-project.yml @@ -1,21 +1,18 @@ name: Add to Project on: - pull_request_target: # zizmor: ignore[dangerous-triggers] + pull_request_target: issues: types: - opened - reopened -permissions: {} - jobs: add-to-project: name: Add to project runs-on: ubuntu-latest - timeout-minutes: 5 steps: - - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0 + - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 with: project-url: https://github.com/orgs/fastapi/projects/2 - github-token: ${{ secrets.PROJECTS_TOKEN }} # zizmor: ignore[secrets-outside-env] + github-token: ${{ secrets.PROJECTS_TOKEN }} diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml deleted file mode 100644 index 84257bbc..00000000 --- a/.github/workflows/deploy-production.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Deploy to Production - -on: - release: - types: - - published - -permissions: {} - -jobs: - deploy: - environment: production - # 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_PRODUCTION }} build - - run: docker compose -f 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 bb0c1dc2..00000000 --- a/.github/workflows/deploy-staging.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Deploy to Staging - -on: - push: - branches: - - master - -permissions: {} - -jobs: - deploy: - environment: staging - # 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_STAGING }} build - - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_STAGING }} up -d diff --git a/.github/workflows/detect-conflicts.yml b/.github/workflows/detect-conflicts.yml index b824f8ae..3ac6f65e 100644 --- a/.github/workflows/detect-conflicts.yml +++ b/.github/workflows/detect-conflicts.yml @@ -1,18 +1,15 @@ name: "Conflict detector" on: push: - pull_request_target: # zizmor: ignore[dangerous-triggers] + pull_request_target: types: [synchronize] -permissions: {} - jobs: main: permissions: contents: read pull-requests: write runs-on: ubuntu-latest - timeout-minutes: 5 steps: - name: Check if PRs have merge conflicts uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 diff --git a/.github/workflows/guard-dependencies.yml b/.github/workflows/guard-dependencies.yml deleted file mode 100644 index c3f97c37..00000000 --- a/.github/workflows/guard-dependencies.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Guard Dependencies - -on: - pull_request_target: # zizmor: ignore[dangerous-triggers] -- This workflow only reads context.payload metadata, never checks out PR code - branches: [master] - paths: - - pyproject.toml - - uv.lock - -permissions: - contents: read - issues: write - pull-requests: write - -jobs: - check-author: - runs-on: ubuntu-latest - steps: - - name: Check if author is org member or allowed bot - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const pr = context.payload.pull_request; - const author = pr.user.login; - const assoc = pr.author_association; - - const botAllowlist = new Set(['dependabot[bot]']); - const orgAuthorAssociations = new Set(['MEMBER', 'OWNER']); - - const allowed = - botAllowlist.has(author) || - (assoc != null && orgAuthorAssociations.has(assoc)); - - if (!allowed) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: `This PR modifies dependency files (\`pyproject.toml\` or \`uv.lock\`), which is restricted to members of the **${context.repo.owner}** organization on GitHub.\n\nIf you need a dependency change, please [open a discussion](https://github.com/${context.repo.owner}/${context.repo.repo}/discussions/new) describing what you need and why.\n\nClosing this PR automatically.` - }); - - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - state: 'closed' - }); - - core.setFailed('Dependency changes are restricted to organization members.'); - } else { - console.log(`Author ${author} (author_association=${assoc}) is allowed to make dependency changes.`); - } diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 6cae8435..99a87edd 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -9,21 +9,19 @@ on: issues: types: - labeled - pull_request_target: # zizmor: ignore[dangerous-triggers] + pull_request_target: types: - labeled workflow_dispatch: -permissions: {} +permissions: + issues: write + pull-requests: write jobs: issue-manager: if: github.repository_owner == 'fastapi' runs-on: ubuntu-latest - timeout-minutes: 5 - permissions: - issues: write - pull-requests: write steps: - name: Dump GitHub context env: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index f89d1550..6ba56739 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,6 @@ name: Labels on: - pull_request_target: # zizmor: ignore[dangerous-triggers] + pull_request_target: types: - opened - synchronize @@ -9,15 +9,12 @@ on: - labeled - unlabeled -permissions: {} - jobs: labeler: permissions: contents: read pull-requests: write runs-on: ubuntu-latest - timeout-minutes: 5 steps: - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }} @@ -29,7 +26,6 @@ jobs: permissions: pull-requests: read runs-on: ubuntu-latest - timeout-minutes: 5 steps: - uses: agilepathway/label-checker@c3d16ad512e7cea5961df85ff2486bb774caf3c5 # v1.6.65 with: diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml index 18076549..5424ca22 100644 --- a/.github/workflows/latest-changes.yml +++ b/.github/workflows/latest-changes.yml @@ -1,7 +1,7 @@ name: Latest Changes on: - pull_request_target: # zizmor: ignore[dangerous-triggers] + pull_request_target: branches: - master types: @@ -16,15 +16,11 @@ on: required: false default: "false" -permissions: {} - jobs: latest-changes: runs-on: ubuntu-latest - timeout-minutes: 5 permissions: pull-requests: read - if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true steps: - name: Dump GitHub context env: @@ -33,8 +29,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # To allow latest-changes to commit to the main branch - token: ${{ secrets.LATEST_CHANGES }} # zizmor: ignore[secrets-outside-env] - persist-credentials: true # required by tiangolo/latest-changes + token: ${{ secrets.LATEST_CHANGES }} - uses: tiangolo/latest-changes@c9d329cb147f0ddf4fb631214e3f838ff17ccbbd # 0.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 78240067..639ac9fc 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -15,19 +15,14 @@ on: required: false default: 'false' -permissions: {} - jobs: changes: runs-on: ubuntu-latest - timeout-minutes: 5 # Set job outputs to values from filter step outputs: changed: ${{ steps.filter.outputs.changed }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false # For pull requests it's not necessary to checkout the code but for the main branch it is - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 id: filter @@ -44,7 +39,7 @@ jobs: needs: - changes if: ${{ needs.changes.outputs.changed == 'true' }} - timeout-minutes: 15 + timeout-minutes: 60 runs-on: ubuntu-latest strategy: matrix: @@ -53,11 +48,7 @@ jobs: fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: 1.3.12 + - uses: oven-sh/setup-bun@v2 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.10' @@ -67,11 +58,7 @@ jobs: with: limit-access-to-actor: true - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - with: - # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. - # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 - version: "0.11.4" + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 - run: uv sync working-directory: backend - run: bun ci @@ -98,14 +85,9 @@ jobs: # Merge reports after playwright-tests, even if some shards have failed if: ${{ !cancelled() && needs.changes.outputs.changed == 'true' }} runs-on: ubuntu-latest - timeout-minutes: 5 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: 1.3.12 + - uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun ci - name: Download blob reports from GitHub Actions Artifacts @@ -131,7 +113,6 @@ jobs: needs: - test-playwright runs-on: ubuntu-latest - timeout-minutes: 5 steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index cdbacd4a..d3bb89e3 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,8 +6,6 @@ on: - opened - synchronize -permissions: {} - env: # Forks and Dependabot don't have access to secrets HAS_SECRETS: ${{ secrets.PRE_COMMIT != '' }} @@ -15,7 +13,6 @@ env: jobs: pre-commit: runs-on: ubuntu-latest - timeout-minutes: 5 steps: - name: Dump GitHub context env: @@ -31,8 +28,7 @@ jobs: # And it needs the full history to be able to compute diffs fetch-depth: 0 # A token other than the default GITHUB_TOKEN is needed to be able to trigger CI - token: ${{ secrets.PRE_COMMIT }} # zizmor: ignore[secrets-outside-env] - persist-credentials: true # Required for `git push` command + token: ${{ secrets.PRE_COMMIT }} # pre-commit lite ci needs the default checkout configs to work - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 name: Checkout PR for fork @@ -41,20 +37,14 @@ jobs: # To be able to commit it needs the head branch of the PR, the remote one ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - persist-credentials: false - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: 1.3.12 + - uses: oven-sh/setup-bun@v2 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.11" - name: Setup uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: - # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. - # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 - version: "0.11.4" cache-dependency-glob: | requirements**.txt pyproject.toml @@ -65,7 +55,7 @@ jobs: run: bun ci - name: Run prek - pre-commit id: precommit - run: uv run prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure + run: uvx prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure continue-on-error: true - name: Commit and push changes if: env.HAS_SECRETS == 'true' @@ -93,7 +83,6 @@ jobs: needs: - pre-commit runs-on: ubuntu-latest - timeout-minutes: 5 steps: - name: Dump GitHub context env: diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index ea3941fe..c9b4da97 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -1,48 +1,34 @@ name: Smokeshow on: - workflow_run: # zizmor: ignore[dangerous-triggers] + workflow_run: workflows: [Test Backend] types: [completed] -permissions: {} - jobs: smokeshow: runs-on: ubuntu-latest - timeout-minutes: 5 permissions: actions: read statuses: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" - - name: Setup uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - with: - # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. - # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 - version: "0.11.4" - cache-dependency-glob: | - pyproject.toml - uv.lock - - run: uv sync --all-packages --no-dev --group github-actions + - run: pip install smokeshow - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: coverage-html path: backend/htmlcov github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - - run: uv run smokeshow upload backend/htmlcov + - 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 }} # zizmor: ignore[secrets-outside-env] + SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }} diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 5fab6320..9558ab72 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -9,27 +9,18 @@ on: - opened - synchronize -permissions: {} - jobs: test-backend: runs-on: ubuntu-latest - timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.10" - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - with: - # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. - # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 - version: "0.11.4" + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 - run: docker compose down -v --remove-orphans - run: docker compose up -d db mailcatcher - name: Migrate DB diff --git a/.github/workflows/test-docker-compose.yml b/.github/workflows/test-docker-compose.yml index a3d4acd2..add87212 100644 --- a/.github/workflows/test-docker-compose.yml +++ b/.github/workflows/test-docker-compose.yml @@ -9,18 +9,13 @@ on: - opened - synchronize -permissions: {} - jobs: test-docker-compose: runs-on: ubuntu-latest - timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - run: docker compose build - run: docker compose down -v --remove-orphans - run: docker compose up -d --wait backend frontend adminer diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml deleted file mode 100644 index 8ec2290b..00000000 --- a/.github/workflows/zizmor.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Zizmor - -on: - push: - branches: - - master - workflow_dispatch: - -permissions: {} - -jobs: - zizmor: - name: Run zizmor - runs-on: ubuntu-latest - timeout-minutes: 5 - permissions: - security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files. - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Run zizmor - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7889ad84..4f18ae22 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,11 +67,3 @@ repos: entry: uv run python scripts/add_latest_release_date.py files: ^release-notes\.md$ pass_filenames: false - - - id: zizmor - name: zizmor - language: python - entry: uv run zizmor . - files: ^\.github\/workflows\/ - require_serial: true - pass_filenames: false diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 24eae850..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Debug FastAPI Project backend: Python Debugger", - "type": "debugpy", - "request": "launch", - "module": "uvicorn", - "args": [ - "app.main:app", - "--reload" - ], - "cwd": "${workspaceFolder}/backend", - "jinja": true, - "envFile": "${workspaceFolder}/.env", - }, - { - "type": "chrome", - "request": "launch", - "name": "Debug Frontend: Launch Chrome against http://localhost:5173", - "url": "http://localhost:5173", - "webRoot": "${workspaceFolder}/frontend" - }, - ] -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0460be51..7e725a3f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,9 +13,6 @@ For small, straightforward changes, you can go directly to a Pull Request withou - Fixing lint warnings or type errors - Minor code improvements (e.g., removing unused code) -Note that PRs from non-team members are not allowed to modify `pyproject.toml` or `uv.lock`, to prevent supply chain risk. -If you would like to add a new dependency, create a new [Discussion](https://github.com/fastapi/full-stack-fastapi-template/discussions) to explain why. - ## Developing For detailed instructions on setting up your development environment, running the stack, linting, pre-commit hooks, and more, see the [Development Guide](development.md). diff --git a/README.md b/README.md index 22165dd6..44e5116e 100644 --- a/README.md +++ b/README.md @@ -6,51 +6,52 @@ ## Deploy to Render -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/render-examples/full-stack-fastapi-template) +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/Ho1yShif/full-stack-fastapi-template) One click provisions the full stack on [Render](https://render.com) using the [`render.yaml`](./render.yaml) Blueprint at the repo root: a Dockerized FastAPI web service, a Vite/Bun static site for the frontend, a managed Postgres database, and a shared `fastapi-env` environment-variable group that both services pull from. ### Setup after clicking the button -1. **Fill in the secret env vars on `fastapi-backend`** (Dashboard โ†’ `fastapi-backend` โ†’ Environment): +1. **Fill in the placeholder values in the `fastapi-env` env group** (Dashboard โ†’ Env Groups โ†’ `fastapi-env`): - `FIRST_SUPERUSER` โ€” admin email, e.g. `admin@yourdomain.com` - `FIRST_SUPERUSER_PASSWORD` โ€” generate one with `python -c "import secrets; print(secrets.token_urlsafe(32))"` + - `DOMAIN` โ€” your custom domain, or any placeholder if you'll use the `.onrender.com` URLs - Optional: `SMTP_HOST` / `SMTP_USER` / `SMTP_PASSWORD` / `EMAILS_FROM_EMAIL` (if you want password-reset emails) and `SENTRY_DSN` (if using Sentry) - `SECRET_KEY` and all `POSTGRES_*` vars are set automatically. `ENVIRONMENT`, `PROJECT_NAME`, `STACK_NAME`, and the `SMTP_TLS/SSL/PORT` defaults come from the `fastapi-env` env group โ€” no action needed. + `SECRET_KEY`, all `POSTGRES_*` vars, `ENVIRONMENT`, `PROJECT_NAME`, `STACK_NAME`, and the `SMTP_TLS/SSL/PORT` defaults are all set automatically โ€” no action needed. - > **Note:** secrets live on the service (not the env group) because Render's env groups don't accept `sync: false` placeholders. The `fastapi-env` group only holds shared, non-secret defaults; everything secret or service-specific is set directly on `fastapi-backend` or `fastapi-frontend`. + > **Note:** the `POSTGRES_*` vars are wired directly to the `fastapi-backend` service via `fromDatabase` references (Render's env groups don't support database links), so they won't show up on the `fastapi-env` group page. You'll find them under `fastapi-backend` โ†’ Environment in the Dashboard, alongside the inherited group vars. They all end up in the same `os.environ` at runtime โ€” the split is purely a Dashboard organizational thing. 2. **Wait for both services to finish deploying** so they're assigned `.onrender.com` URLs. -3. **Set the cross-service URLs on the appropriate services** (these can only be set after the first deploy, since they depend on the assigned hostnames): - - `VITE_API_URL` โ†’ set on **`fastapi-frontend`** โ†’ Environment, to the **backend** URL, e.g. `https://fastapi-backend-XXXX.onrender.com`. *Baked into the frontend bundle at build time.* - - `FRONTEND_HOST` โ†’ set on **`fastapi-backend`** โ†’ Environment, to the **frontend** URL, e.g. `https://fastapi-frontend-XXXX.onrender.com`. *Auto-appended to the backend's CORS allowlist at runtime.* - - `BACKEND_CORS_ORIGINS` โ†’ set on **`fastapi-backend`** โ†’ Environment, comma-separated list of additional allowed origins (you can usually leave this empty since `FRONTEND_HOST` already covers the frontend). +3. **Go back to the `fastapi-env` env group and fill in the cross-service URLs** (these can only be set after the first deploy, since they depend on the assigned hostnames). All three live in the env group, but they're consumed by different services: + - `VITE_API_URL` โ†’ set to the **backend** URL, e.g. `https://fastapi-backend-XXXX.onrender.com`. *Read by the **frontend** service at build time โ€” baked into the bundle.* + - `FRONTEND_HOST` โ†’ set to the **frontend** URL, e.g. `https://fastapi-frontend-XXXX.onrender.com`. *Read by the **backend** service at runtime โ€” auto-appended to the CORS allowlist.* + - `BACKEND_CORS_ORIGINS` โ†’ comma-separated list of additional allowed origins (you can usually leave this empty since `FRONTEND_HOST` already covers the frontend). *Read by the **backend** service at runtime.* 4. **Trigger a manual rebuild of the frontend** (Dashboard โ†’ `fastapi-frontend` โ†’ Manual Deploy โ†’ Clear build cache & deploy). `VITE_API_URL` is baked into the bundle at build time, so an existing build won't pick up the new value until rebuilt. ## 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](https://vitejs.dev), and other parts of a modern frontend stack. - - [Tailwind CSS](https://tailwindcss.com) and [shadcn/ui](https://ui.shadcn.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. -- [Mailcatcher](https://mailcatcher.me) for local email testing during development. -- 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. +- โšก [**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](https://vitejs.dev), and other parts of a modern frontend stack. + - ๐ŸŽจ [Tailwind CSS](https://tailwindcss.com) and [shadcn/ui](https://ui.shadcn.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. +- ๐Ÿ“ฌ [Mailcatcher](https://mailcatcher.me) for local email testing during development. +- โœ… 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 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..0045fb81 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# 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/pyproject.toml b/backend/pyproject.toml index 326150d5..89a5bdd7 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -5,7 +5,7 @@ description = "" requires-python = ">=3.10,<4.0" dependencies = [ "fastapi[standard]<1.0.0,>=0.114.2", - "python-multipart<1.0.0,>=0.0.27", + "python-multipart<1.0.0,>=0.0.7", "email-validator<3.0.0.0,>=2.1.0.post1", "tenacity<9.0.0,>=8.2.3", "pydantic>2.0", diff --git a/backend/scripts/start.sh b/backend/scripts/start.sh index 4079c0c4..5715eb21 100644 --- a/backend/scripts/start.sh +++ b/backend/scripts/start.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -e bash scripts/prestart.sh -exec fastapi run --workers "${WEB_CONCURRENCY:-1}" --host 0.0.0.0 --port "$PORT" app/main.py +exec fastapi run --workers 4 --host 0.0.0.0 --port "$PORT" app/main.py diff --git a/bun.lock b/bun.lock index 34d6b22a..f4872bf5 100644 --- a/bun.lock +++ b/bun.lock @@ -49,7 +49,7 @@ "@hey-api/openapi-ts": "0.73.0", "@playwright/test": "1.58.2", "@tanstack/router-devtools": "^1.166.7", - "@tanstack/router-plugin": "^1.140.0", + "@tanstack/router-plugin": "^1.166.7", "@types/node": "^25.5.0", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", @@ -406,23 +406,23 @@ "@tanstack/react-table": ["@tanstack/react-table@8.21.3", "", { "dependencies": { "@tanstack/table-core": "8.21.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww=="], - "@tanstack/router-core": ["@tanstack/router-core@1.153.2", "", { "dependencies": { "@tanstack/history": "1.153.2", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.1", "seroval-plugins": "^1.4.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-WLaR+rSNW7bj9UCJQ3SKpuh6nZBZkpGnf2mpjn/uRB6joIQ3BU7aRdhb7w9Via/MP52iaHh5sd8NY3MaLpF2tQ=="], + "@tanstack/router-core": ["@tanstack/router-core@1.169.2", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw=="], "@tanstack/router-devtools": ["@tanstack/router-devtools@1.166.7", "", { "dependencies": { "@tanstack/react-router-devtools": "1.166.7", "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/react-router": "^1.166.7", "csstype": "^3.0.10", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["csstype"] }, "sha512-N1HzWEe5bz7ID35Fq1ZKLEQpyaOhdJBYY1kJ+irNBaxMlNEKM3A+SkmVuUpj6H2kmsfHe5YDrj5YYV9tJAGfug=="], "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.166.7", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.166.7", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-/OGLZlrw5NSNd9/PTL8vPSpmjxIbXNoeJATMHlU3YLCBVBtLx41CHIRc7OLkjyfVFJ4Sq7Pq+2/YH8PChShefg=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.153.2", "", { "dependencies": { "@tanstack/router-core": "1.153.2", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-bEhmCtXq5vv3HukKq5zmTDBNDRqVllYxsHoWtqEvHv5hCb5xwKKfUMGemRoiQ96/wLFuGnA5DYkem2GZWcG3wg=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.166.42", "", { "dependencies": { "@babel/types": "^7.28.5", "@tanstack/router-core": "1.169.2", "@tanstack/router-utils": "1.161.8", "@tanstack/virtual-file-routes": "1.161.7", "jiti": "^2.7.0", "magic-string": "^0.30.21", "prettier": "^3.5.0", "zod": "^3.24.2" } }, "sha512-2qBWC0t78r6b3vI+AbnvCZcFAvbYBDlLuWZrTjQbcjUmwG3qyeQp983tJyDuj9wb5//adG1tgAGXZkJ3aDwdBg=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.153.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.153.2", "@tanstack/router-generator": "1.153.2", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "babel-dead-code-elimination": "^1.0.11", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.153.2", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "vite-plugin-solid", "webpack"] }, "sha512-aMMc70ChM0wBYOToq39kTMKI2A0EKWpumiKTJyAwEglXf0raF48+26Fmv0gr9/5CLvD0g8ljllsskVDyzg8oDw=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.167.35", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.169.2", "@tanstack/router-generator": "1.166.42", "@tanstack/router-utils": "1.161.8", "@tanstack/virtual-file-routes": "1.161.7", "chokidar": "^3.6.0", "unplugin": "^3.0.0", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2 || ^2.0.0", "@tanstack/react-router": "^1.169.2", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-UAScU5VAzLYVY4FML/Cbc5S5TucT4I8Ata05yozGOe4ZfepTKRffA5xWLtD2N+ov5svdv0KTX/kqlZnYPe28mA=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.143.11", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-N24G4LpfyK8dOlnP8BvNdkuxg1xQljkyl6PcrdiPSA301pOjatRT1y8wuCCJZKVVD8gkd0MpCZ0VEjRMGILOtA=="], + "@tanstack/router-utils": ["@tanstack/router-utils@1.161.8", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-xyiLWEKjfBAVhauDSSjXxyf7s8elU6SM+V050sbkofvGmIIvkwPFtDsX7Gvwh14kBd6iCwAT+RiPvXTxAptY0Q=="], - "@tanstack/store": ["@tanstack/store@0.8.0", "", {}, "sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ=="], + "@tanstack/store": ["@tanstack/store@0.9.1", "", {}, "sha512-+qcNkOy0N1qSGsP7omVCW0SDrXtaDcycPqBDE726yryiA5eTDFpjBReaYjghVJwNf1pcPMyzIwTGlYjCSQR0Fg=="], "@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="], - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.145.4", "", {}, "sha512-CI75JrfqSluhdGwLssgVeQBaCphgfkMQpi8MCY3UJX1hoGzXa8kHYJcUuIFMOLs1q7zqHy++EVVtMK03osR5wQ=="], + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], @@ -448,8 +448,6 @@ "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], - "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="], @@ -494,7 +492,7 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="], + "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], @@ -538,8 +536,6 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -718,8 +714,6 @@ "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], "rollup": ["rollup@4.55.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.2", "@rollup/rollup-android-arm64": "4.55.2", "@rollup/rollup-darwin-arm64": "4.55.2", "@rollup/rollup-darwin-x64": "4.55.2", "@rollup/rollup-freebsd-arm64": "4.55.2", "@rollup/rollup-freebsd-x64": "4.55.2", "@rollup/rollup-linux-arm-gnueabihf": "4.55.2", "@rollup/rollup-linux-arm-musleabihf": "4.55.2", "@rollup/rollup-linux-arm64-gnu": "4.55.2", "@rollup/rollup-linux-arm64-musl": "4.55.2", "@rollup/rollup-linux-loong64-gnu": "4.55.2", "@rollup/rollup-linux-loong64-musl": "4.55.2", "@rollup/rollup-linux-ppc64-gnu": "4.55.2", "@rollup/rollup-linux-ppc64-musl": "4.55.2", "@rollup/rollup-linux-riscv64-gnu": "4.55.2", "@rollup/rollup-linux-riscv64-musl": "4.55.2", "@rollup/rollup-linux-s390x-gnu": "4.55.2", "@rollup/rollup-linux-x64-gnu": "4.55.2", "@rollup/rollup-linux-x64-musl": "4.55.2", "@rollup/rollup-openbsd-x64": "4.55.2", "@rollup/rollup-openharmony-arm64": "4.55.2", "@rollup/rollup-win32-arm64-msvc": "4.55.2", "@rollup/rollup-win32-ia32-msvc": "4.55.2", "@rollup/rollup-win32-x64-gnu": "4.55.2", "@rollup/rollup-win32-x64-msvc": "4.55.2", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-PggGy4dhwx5qaW+CKBilA/98Ql9keyfnb7lh4SR6shQ91QQQi1ORJ1v4UinkdP2i87OBs9AQFooQylcrrRfIcg=="], @@ -730,9 +724,9 @@ "semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "seroval": ["seroval@1.4.2", "", {}, "sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ=="], + "seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="], - "seroval-plugins": ["seroval-plugins@1.4.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA=="], + "seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="], "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], @@ -772,7 +766,7 @@ "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], - "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], + "unplugin": ["unplugin@3.0.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], @@ -866,13 +860,9 @@ "@tanstack/react-router/@tanstack/router-core": ["@tanstack/router-core@1.163.3", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-jPptiGq/w3nuPzcMC7RNa79aU+b6OjaDzWJnBcV2UAwL4ThJamRS4h42TdhJE+oF5yH9IEnCOGQdfnbw45LbfA=="], - "@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.9.1", "", {}, "sha512-+qcNkOy0N1qSGsP7omVCW0SDrXtaDcycPqBDE726yryiA5eTDFpjBReaYjghVJwNf1pcPMyzIwTGlYjCSQR0Fg=="], + "@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="], - "@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.153.2", "", {}, "sha512-TVa0Wju5w6JZGq/S74Q7TQNtKXDatJaB4NYrhMZVU9ETlkgpr35NhDfOzsCJ93P0KCo1ZoDodlFp3c54/dLsyw=="], - - "@tanstack/router-devtools-core/@tanstack/router-core": ["@tanstack/router-core@1.163.3", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-jPptiGq/w3nuPzcMC7RNa79aU+b6OjaDzWJnBcV2UAwL4ThJamRS4h42TdhJE+oF5yH9IEnCOGQdfnbw45LbfA=="], - - "@tanstack/router-generator/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@tanstack/router-generator/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], "@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -928,9 +918,11 @@ "@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@tanstack/react-router/@tanstack/router-core/@tanstack/store": ["@tanstack/store@0.9.1", "", {}, "sha512-+qcNkOy0N1qSGsP7omVCW0SDrXtaDcycPqBDE726yryiA5eTDFpjBReaYjghVJwNf1pcPMyzIwTGlYjCSQR0Fg=="], + "@tanstack/react-router/@tanstack/router-core/cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="], + + "@tanstack/react-router/@tanstack/router-core/seroval": ["seroval@1.4.2", "", {}, "sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ=="], - "@tanstack/router-devtools-core/@tanstack/router-core/@tanstack/store": ["@tanstack/store@0.9.1", "", {}, "sha512-+qcNkOy0N1qSGsP7omVCW0SDrXtaDcycPqBDE726yryiA5eTDFpjBReaYjghVJwNf1pcPMyzIwTGlYjCSQR0Fg=="], + "@tanstack/react-router/@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.4.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA=="], "c12/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], } diff --git a/compose.override.yml b/compose.override.yml index 779cc823..5020f09d 100644 --- a/compose.override.yml +++ b/compose.override.yml @@ -6,7 +6,7 @@ services: # etc. To enable it, update .env, set: # DOMAIN=localhost.tiangolo.com proxy: - image: traefik:3.6 + image: traefik:v3.7 volumes: - /var/run/docker.sock:/var/run/docker.sock ports: diff --git a/compose.traefik.yml b/compose.traefik.yml index bcd7d142..8c32c455 100644 --- a/compose.traefik.yml +++ b/compose.traefik.yml @@ -1,6 +1,6 @@ services: traefik: - image: traefik:3.6 + image: traefik:v3.7 ports: # Listen on port 80, default for HTTP, necessary to redirect to HTTPS - 80:80 diff --git a/deployment.md b/deployment.md index 6d49d6a9..4b8ebc19 100644 --- a/deployment.md +++ b/deployment.md @@ -288,15 +288,9 @@ cd /home/github/actions-runner 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). -### Configure GitHub Environments - -The deployment workflows use [GitHub Environments](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments) for `staging` and `production`. This enables environment-specific secrets, deployment protection rules (e.g. required reviewers, wait timers), and deployment status tracking. - -To configure them, go to your repository's **Settings** > **Environments** and create the `staging` and `production` environments. - ### Set Secrets -For each GitHub Environment (`staging` and `production`), configure the required secrets as [environment secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-an-environment). Environment secrets are preferred over [repository secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-a-repository) because they are scoped to the specific environment, reducing exposure and aligning with any protection rules you configure. +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: @@ -319,8 +313,6 @@ There are GitHub Action workflows in the `.github/workflows` directory already c * `staging`: after pushing (or merging) to the branch `master`. * `production`: after publishing a release. -Both workflows are associated with their respective GitHub Environments, so deployments will be visible in the repository's **Environments** section and will respect any protection rules you configure. - If you need to add extra environments you could use those as a starting point. ## URLs diff --git a/frontend/package.json b/frontend/package.json index f3b8d23e..9b69525b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,7 +53,7 @@ "@hey-api/openapi-ts": "0.73.0", "@playwright/test": "1.58.2", "@tanstack/router-devtools": "^1.166.7", - "@tanstack/router-plugin": "^1.140.0", + "@tanstack/router-plugin": "^1.166.7", "@types/node": "^25.5.0", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 8849130b..08d665fe 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -65,6 +65,7 @@ const LayoutAdminRoute = LayoutAdminRouteImport.update({ } as any) export interface FileRoutesByFullPath { + '/': typeof LayoutIndexRoute '/login': typeof LoginRoute '/recover-password': typeof RecoverPasswordRoute '/reset-password': typeof ResetPasswordRoute @@ -72,7 +73,6 @@ export interface FileRoutesByFullPath { '/admin': typeof LayoutAdminRoute '/items': typeof LayoutItemsRoute '/settings': typeof LayoutSettingsRoute - '/': typeof LayoutIndexRoute } export interface FileRoutesByTo { '/login': typeof LoginRoute @@ -99,6 +99,7 @@ export interface FileRoutesById { export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: + | '/' | '/login' | '/recover-password' | '/reset-password' @@ -106,7 +107,6 @@ export interface FileRouteTypes { | '/admin' | '/items' | '/settings' - | '/' fileRoutesByTo: FileRoutesByTo to: | '/login' @@ -171,7 +171,7 @@ declare module '@tanstack/react-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } diff --git a/pyproject.toml b/pyproject.toml index 3d7745e7..39355497 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,2 @@ [tool.uv.workspace] members = ["backend"] - -[dependency-groups] -dev = [ - "zizmor>=1.23.1", -] - -github-actions = [ - "smokeshow >=0.5.0", -] diff --git a/release-notes.md b/release-notes.md index 3d22a8fc..b4898ec9 100644 --- a/release-notes.md +++ b/release-notes.md @@ -13,19 +13,10 @@ ### Docs -* ๐Ÿ“ Update security policy. PR [#2297](https://github.com/fastapi/full-stack-fastapi-template/pull/2297) by [@tiangolo](https://github.com/tiangolo). * ๐Ÿ“ Add `CONTRIBUTING.md`. PR [#2159](https://github.com/fastapi/full-stack-fastapi-template/pull/2159) by [@alejsdev](https://github.com/alejsdev). ### Internal -* ๐Ÿ‘ท Configure Dependabot to group updates and update weekly. PR [#2293](https://github.com/fastapi/full-stack-fastapi-template/pull/2293) by [@YuriiMotov](https://github.com/YuriiMotov). -* ๐Ÿ”ฅ Remove config files now in central GitHub repo. PR [#2300](https://github.com/fastapi/full-stack-fastapi-template/pull/2300) by [@tiangolo](https://github.com/tiangolo). -* โฌ† Bump actions/add-to-project from 1.0.2 to 2.0.0. PR [#2273](https://github.com/fastapi/full-stack-fastapi-template/pull/2273) by [@dependabot[bot]](https://github.com/apps/dependabot). -* โฌ† Bump python-multipart from 0.0.21 to 0.0.27. PR [#2277](https://github.com/fastapi/full-stack-fastapi-template/pull/2277) by [@dependabot[bot]](https://github.com/apps/dependabot). -* โฌ† Bump idna from 3.11 to 3.15. PR [#2294](https://github.com/fastapi/full-stack-fastapi-template/pull/2294) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ๐Ÿ”’๏ธ Only allow team members to modify dependencies. PR [#2292](https://github.com/fastapi/full-stack-fastapi-template/pull/2292) by [@svlandeg](https://github.com/svlandeg). -* โฌ† Bump urllib3 from 2.6.3 to 2.7.0. PR [#2282](https://github.com/fastapi/full-stack-fastapi-template/pull/2282) by [@dependabot[bot]](https://github.com/apps/dependabot). -* ๐Ÿ”’๏ธ Add zizmor and fix audit findings. PR [#2260](https://github.com/fastapi/full-stack-fastapi-template/pull/2260) by [@YuriiMotov](https://github.com/YuriiMotov). * ๐Ÿ”’ Pin GitHub actions by commit SHA. PR [#2246](https://github.com/fastapi/full-stack-fastapi-template/pull/2246) by [@YuriiMotov](https://github.com/YuriiMotov). * ๐Ÿ”จ Add pre-commit hook to ensure latest release header has date. PR [#2205](https://github.com/fastapi/full-stack-fastapi-template/pull/2205) by [@YuriiMotov](https://github.com/YuriiMotov). * ๐Ÿ‘ท Add `ty` to precommit. PR [#2227](https://github.com/fastapi/full-stack-fastapi-template/pull/2227) by [@svlandeg](https://github.com/svlandeg). diff --git a/render.yaml b/render.yaml index c345b39d..098b2dfc 100644 --- a/render.yaml +++ b/render.yaml @@ -13,6 +13,30 @@ envVarGroups: value: "false" - key: SMTP_PORT value: "587" + - key: SECRET_KEY + generateValue: true + - key: FIRST_SUPERUSER + sync: false + - key: FIRST_SUPERUSER_PASSWORD + sync: false + - key: DOMAIN + sync: false + - key: FRONTEND_HOST + sync: false + - key: BACKEND_CORS_ORIGINS + sync: false + - key: SMTP_HOST + sync: false + - key: SMTP_USER + sync: false + - key: SMTP_PASSWORD + sync: false + - key: EMAILS_FROM_EMAIL + sync: false + - key: SENTRY_DSN + sync: false + - key: VITE_API_URL + sync: false projects: - name: full-stack-fastapi-template @@ -23,8 +47,8 @@ projects: name: fastapi-backend runtime: docker plan: free - repo: https://github.com/render-examples/full-stack-fastapi-template - branch: main + repo: https://github.com/Ho1yShif/full-stack-fastapi-template + branch: master autoDeploy: true dockerfilePath: ./backend/Dockerfile dockerContext: . @@ -32,8 +56,6 @@ projects: healthCheckPath: /api/v1/utils/health-check/ envVars: - fromGroup: fastapi-env - - key: SECRET_KEY - generateValue: true - key: POSTGRES_SERVER fromDatabase: name: fastapi-db @@ -54,30 +76,12 @@ projects: fromDatabase: name: fastapi-db property: database - - key: FIRST_SUPERUSER - sync: false - - key: FIRST_SUPERUSER_PASSWORD - sync: false - - key: FRONTEND_HOST - sync: false - - key: BACKEND_CORS_ORIGINS - sync: false - - key: SMTP_HOST - sync: false - - key: SMTP_USER - sync: false - - key: SMTP_PASSWORD - sync: false - - key: EMAILS_FROM_EMAIL - sync: false - - key: SENTRY_DSN - sync: false - type: web name: fastapi-frontend runtime: static - repo: https://github.com/render-examples/full-stack-fastapi-template - branch: main + repo: https://github.com/Ho1yShif/full-stack-fastapi-template + branch: master autoDeploy: true buildCommand: bun install --frozen-lockfile && bun run --filter frontend build staticPublishPath: frontend/dist @@ -86,8 +90,7 @@ projects: source: /* destination: /index.html envVars: - - key: VITE_API_URL - sync: false + - fromGroup: fastapi-env databases: - name: fastapi-db diff --git a/uv.lock b/uv.lock index 407700a9..31203534 100644 --- a/uv.lock +++ b/uv.lock @@ -11,10 +11,6 @@ members = [ "app", ] -[manifest.dependency-groups] -dev = [{ name = "zizmor", specifier = ">=1.23.1" }] -github-actions = [{ name = "smokeshow", specifier = ">=0.5.0" }] - [[package]] name = "alembic" version = "1.18.1" @@ -107,7 +103,7 @@ requires-dist = [ { name = "pydantic", specifier = ">2.0" }, { name = "pydantic-settings", specifier = ">=2.2.1,<3.0.0" }, { name = "pyjwt", specifier = ">=2.8.0,<3.0.0" }, - { name = "python-multipart", specifier = ">=0.0.27,<1.0.0" }, + { name = "python-multipart", specifier = ">=0.0.7,<1.0.0" }, { name = "sentry-sdk", extras = ["fastapi"], specifier = ">=2.0.0,<3.0.0" }, { name = "sqlmodel", specifier = ">=0.0.21,<1.0.0" }, { name = "tenacity", specifier = ">=8.2.3,<9.0.0" }, @@ -939,11 +935,11 @@ wheels = [ [[package]] name = "idna" -version = "3.15" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -1742,11 +1738,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.27" +version = "0.0.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/9b/f23807317a113dc36e74e75eb265a02dd1a4d9082abc3c1064acd22997c4/python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602", size = 44043, upload-time = "2026-04-27T10:51:26.649Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196, upload-time = "2025-12-17T09:24:22.446Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/78/4126abcbdbd3c559d43e0db7f7b9173fc6befe45d39a2856cc0b8ec2a5a6/python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645", size = 29254, upload-time = "2026-04-27T10:51:24.997Z" }, + { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541, upload-time = "2025-12-17T09:24:21.153Z" }, ] [[package]] @@ -2038,19 +2034,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "smokeshow" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/94/c99b76517c268ef8d5c2ff88faba5a019664bd69e4754944afa294b4f24c/smokeshow-0.5.0.tar.gz", hash = "sha256:91dcabc29ac3116bff59b4d8a7bda4ae3ccc4c70742a38cec7127b8162e4a0f6", size = 101349, upload-time = "2025-01-07T19:41:51.732Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/10/0d23e4953eb7c1e1ad848084b3115f19234f34f907658ed11bed0d826aee/smokeshow-0.5.0-py3-none-any.whl", hash = "sha256:da12a960fc7cb525efc4035a0c3c9363b6217ea7e66bc39b9ed3cd8bed6eeedc", size = 8389, upload-time = "2025-01-07T19:41:49.194Z" }, -] - [[package]] name = "sqlalchemy" version = "2.0.45" @@ -2259,11 +2242,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.7.0" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] @@ -2505,21 +2488,3 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] - -[[package]] -name = "zizmor" -version = "1.23.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/58/d0228b1332f001f905d3cdd288a878d339e740ef8a92c321696a7359bdcd/zizmor-1.23.1.tar.gz", hash = "sha256:eb9871f1de004d8c6e35ff403bd6a41c495062736e78b9c4a98988970c598639", size = 463942, upload-time = "2026-03-08T16:57:29.065Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/57/32893d3370aa39f140934ee346a77aff1bc38d1de5248b9385dfcea612b7/zizmor-1.23.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:85f222eb610379aeeea76e4dc616621fdae9f21db77d1b006820452cafa739eb", size = 9085239, upload-time = "2026-03-08T16:57:32.241Z" }, - { url = "https://files.pythonhosted.org/packages/e3/43/037b68a2d173a44286f27c5c47e219d8beba758a323e1642770956831732/zizmor-1.23.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:82a7925bbdbc69713cbeb19ec90012cba3b92e3ace65ae60088e9604c5724182", size = 8657180, upload-time = "2026-03-08T16:57:23.078Z" }, - { url = "https://files.pythonhosted.org/packages/e5/37/322ec0e8b8d39a7de30290b754bd564c0b1c432d72f7b7aa011eca87cc7b/zizmor-1.23.1-py3-none-manylinux_2_24_aarch64.whl", hash = "sha256:19af913bb4bcd6dfeea41477fcf203d69e053f4b14a2b35690485c44ffa6c4a7", size = 8788247, upload-time = "2026-03-08T16:57:18.477Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e7/5ca6f7d56741b190c6d7d3721eb98c66e23fb68d64e6886c92993e049f36/zizmor-1.23.1-py3-none-manylinux_2_28_armv7l.whl", hash = "sha256:08ae0d8f4d665f6cf9b475913c64d2193d52ffc6f02ce66d4dcfd1b92daf4f82", size = 8374212, upload-time = "2026-03-08T16:57:25.437Z" }, - { url = "https://files.pythonhosted.org/packages/d4/a5/a3784392aeaca14d65c5e5efa2795d887ba24db4871a942e06a99f90a3c8/zizmor-1.23.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:08233d0d25947e43ac92374f22383c04e43f351f44bc44d60b3c0695157c0f3e", size = 9230697, upload-time = "2026-03-08T16:57:34.425Z" }, - { url = "https://files.pythonhosted.org/packages/b6/0d/4475ded1664262af70525700e158c3156653391770159d65cd80245fb68e/zizmor-1.23.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:795e04dff47ca1d1b0af2d7a5d3a96909a18d5fa80548534951efb24af6ec83e", size = 8820009, upload-time = "2026-03-08T16:57:36.865Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/818c68d9b407e3d02fbe7e39ad73750846d19afad50c4c9ad86455214fc2/zizmor-1.23.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c62059c75100d0bc1a19cd95a6dce9b93ac5ab2e7d7bcdd974c51b2c5eb503e3", size = 8331336, upload-time = "2026-03-08T16:57:20.825Z" }, - { url = "https://files.pythonhosted.org/packages/28/bb/1c984e1474fcf5f08e5847838007668d2682e1fcbc109d481967736ab18f/zizmor-1.23.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf0dc93171e9ae7b822041471715ea7a9f5ebefa6865ceb6d1a39729a982d770", size = 9314682, upload-time = "2026-03-08T16:57:27.361Z" }, - { url = "https://files.pythonhosted.org/packages/fb/26/10f597f9b19ecd7bece2a1eb7d1ca1bd09d089d750d70365c76118056ec1/zizmor-1.23.1-py3-none-win32.whl", hash = "sha256:229c6b275941a18b03eef0ba5d24089dfbbe4fc34633a6b22bf924294ef69cde", size = 7464678, upload-time = "2026-03-08T16:57:30.569Z" }, - { url = "https://files.pythonhosted.org/packages/04/25/14071ea8ab5ebde85391d27e9de060d8a31a44eea448aba8d8bdd30693b3/zizmor-1.23.1-py3-none-win_amd64.whl", hash = "sha256:dc9befe3c08fea7d0fa3a0bc98073fadf31a77f0572b1f7931e1ff300337fe11", size = 8506938, upload-time = "2026-03-08T16:57:15.787Z" }, -]