From 3009a9e1f26cb11c28e62e5d46bbf9b1a9117f4c Mon Sep 17 00:00:00 2001 From: Simon Kelly Date: Mon, 20 May 2024 10:17:28 +0200 Subject: [PATCH 01/16] update docs to reference litellm --- ai.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ai.md b/ai.md index 41600a6..6640b74 100644 --- a/ai.md +++ b/ai.md @@ -12,15 +12,15 @@ This section covers how it works and the various supported options. You can choose between two options for your LLM chat: OpenAI and LLM (generic). The OpenAI option limits you to OpenAI models, but supports streaming and asynchronous API access. -The generic "LLM" option uses the [llm library](https://github.com/simonw/llm) and can be used with many different -models---including local ones. However, it does not yet support streaming responses. +The generic "LLM" option uses the [litellm library](https://docs.litellm.ai/docs/) and can be used with many different +models---including local ones. We recommend choosing "OpenAI" unless you know you want to use a different model. ### Configuring OpenAI If you're using OpenAI, you need to set `OPENAI_API_KEY` in your environment or settings file (`.env` in development). -You can also change the model used by setting `OPENAI_MODEL`, which defualts to `"gpt-3.5-turbo"`. +You can also change the model used by setting `OPENAI_MODEL`, which defaults to `"gpt-3.5-turbo"`. See [this page](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key) for help finding your OpenAI API key. @@ -32,20 +32,19 @@ values in your `settings.py`. For example: ```python LLM_MODELS = { - "gpt4": {"key": env("OPENAI_API_KEY", default="")}, - "claude-3-opus": {"key": env("ANTHROPIC_API_KEY", default="")}, # requires llm-claude-3 - "Meta-Llama-3-8B-Instruct": {}, # requires llm-gpt4all + "gpt-3.5-turbo": {"api_key": env("OPENAI_API_KEY", default="")}, + "gpt4": {"api_key": env("OPENAI_API_KEY", default="")}, + "claude-3-opus-20240229": {"api_key": env("ANTHROPIC_API_KEY", default="")}, + "ollama_chat/llama3": {"api_base": env("OLLAMA_API_BASE", default="http://localhost:11434")}, # requires a running ollama instance } -DEFAULT_LLM_MODEL = "gpt4" +DEFAULT_LLM_MODEL = env("DEFAULT_LLM_MODEL", default="gpt4") ``` The chat UI will use whatever is set in `DEFAULT_LLM_MODEL` out-of-the-box, but you can quickly change it to another model to try different options. -Any models that you add will need to be installed as [llm plugins](https://llm.datasette.io/en/stable/plugins/index.html). -You can do this by putting them in your requirements files, [as outlined here](./python.md#adding-or-removing-a-package). -For example, to use Claude 3 you need to add the [`llm-claude-3` plugin](https://github.com/simonw/llm-claude-3), -and to use local models like Llama 3, you need [`llm-gpt4all`](https://github.com/simonw/llm-gpt4all). +For further reading, see the documentation of the [litellm Python API](https://docs.litellm.ai/docs/completion), +and [litellm providers](https://docs.litellm.ai/docs/providers). For further reading, see the documentation of the [llm Python API](https://llm.datasette.io/en/stable/python-api.html), and [llm generally](https://llm.datasette.io/en/stable/index.html). From 507f5c49bec5c081e6c8baf1f060a1121349fcd5 Mon Sep 17 00:00:00 2001 From: Simon Kelly Date: Mon, 20 May 2024 10:17:37 +0200 Subject: [PATCH 02/16] add docs for setting up ollama --- ai.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ai.md b/ai.md index 6640b74..2ef0b8d 100644 --- a/ai.md +++ b/ai.md @@ -46,8 +46,25 @@ to another model to try different options. For further reading, see the documentation of the [litellm Python API](https://docs.litellm.ai/docs/completion), and [litellm providers](https://docs.litellm.ai/docs/providers). -For further reading, see the documentation of the [llm Python API](https://llm.datasette.io/en/stable/python-api.html), -and [llm generally](https://llm.datasette.io/en/stable/index.html). +### Running open source LLMs +To run models like Mixtral or Llama3, you will need to run an [Ollama](https://ollama.com/) server in a separate process. + +1. [Download](https://ollama.com/download) and run Ollama or use the Docker [image](https://hub.docker.com/r/ollama/ollama) +2. Download the model you want to run: + ```shell + ollama pull llama3 + # or with docker + docker exec -it ollama ollama pull llama3 + ``` + See the [documentation](https://docs.litellm.ai/docs/providers/ollama) for the list of supported models. +3. Update your django settings to point to the Ollama server. For example: + ```python + LLM_MODELS = { + "ollama_chat/llama3": {"api_base": "http://localhost:11434"}, + } + DEFAULT_LLM_MODEL = "ollama_chat/llama3" + ``` +4. Restart your Django server. ### The Chat UI From 775e1abede896836e2406fe3bf8cdb66c0953c2a Mon Sep 17 00:00:00 2001 From: Simon Kelly Date: Mon, 20 May 2024 16:18:54 +0200 Subject: [PATCH 03/16] health check token docs --- deployment/production-checklist.md | 14 +++++++++++++- release-notes.md | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/deployment/production-checklist.md b/deployment/production-checklist.md index 18c828e..b31efc2 100644 --- a/deployment/production-checklist.md +++ b/deployment/production-checklist.md @@ -51,6 +51,7 @@ Ensure that you have configured the following variables (if you are using them): `STRIPE_LIVE_PUBLIC_KEY`, and `STRIPE_LIVE_SECRET_KEY` config vars (or whatever subset you are using). - If you set up email, ensure whatever keys/secrets you need are set. - If you're using Mailchimp, set `MAILCHIMP_API_KEY` and `MAILCHIMP_LIST_ID`. +- If you're using Health Checks, set `HEALTH_CHECK_TOKENS`. Refer to your [chosen platform's documentation](/deployment.rst) for details on how to set environment variables in that platform. @@ -104,7 +105,18 @@ see any errors that are encountered. It's also recommended to enable the health check endpoint and connect it to a monitoring tool like [StatusCake](https://www.statuscake.com/) or [Uptime Robot](https://uptimerobot.com/) so that you can be alerted whenever your site or services are having an outage. -The URL you should connect is: yourdomain.com/health/. +The URL you should connect is: `yourdomain.com/health/`. + +If you have the "Health Check Endpoint" option enabled for your project you should also ensure that +you have set the `HEALTH_CHECK_TOKENS` environment variable to a secure value. This can be a comma-separated +list of tokens that are required to access the health check endpoint: + +``` +yourdomain.com/health/?token=your_secret_token +``` + +You can then use this URL with the monitoring tool to ensure that only your monitoring tool can +access the health check endpoint. ## Double-check your language settings diff --git a/release-notes.md b/release-notes.md index 234dc6f..ef82da1 100644 --- a/release-notes.md +++ b/release-notes.md @@ -49,6 +49,9 @@ and Celery workers and returns a non-200 response code if there are any identifi These endpoints can be connected to a monitoring tool like [StatusCake](https://www.statuscake.com/) or [Uptime Robot](https://uptimerobot.com/) so that you can be alerted whenever your site is having issues. +See the section on [monitoring](./deployment/production-checklist.md#set-up-monitoring) in the +production checklist for more information. + ### Allauth updates The [django-allauth](https://docs.allauth.org/en/latest/) library was updated to the latest version, @@ -3426,4 +3429,3 @@ Documentation for subscriptions can be [found here](/subscriptions). ## Version 0.4 and earlier Release notes for earlier versions are no longer publicly available. - From 51a801304a4aed8076f89230fc1dc6af021d3ab3 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 14 May 2024 09:08:27 +0200 Subject: [PATCH 04/16] release notes dump --- release-notes.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/release-notes.md b/release-notes.md index ef82da1..18fc718 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,6 +3,17 @@ Version History and Release Notes Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus.com/) are documented here. +## Next Release + +- Remove `static/css` and `static/js` directories from the `dockerignore` file so that other project files + can be included. Also updated the production Docker build process so that any existing files are overwritten + by the built versions. (Thanks Raul for reporting!) +- Made some performance improvements to the production Dockerfile build + (don't rebuilding front end if there are no changes in the dependent files). +- Changed behavior when team role checks fail from raising a `TeamPermissionError` to returning a 403 response, + and updated affected tests. This also removes confusing stack traces from successful test runs. +- Removed no longer used `TeamPermissionError` class. + ## Version 2024.5.2 This is a hotfix release that fixes a bug that prevented the team management page From 05474ccfaad96a283263ff186e83cd53c5730b22 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 21 May 2024 10:07:20 +0200 Subject: [PATCH 05/16] add release note --- release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes.md b/release-notes.md index 18fc718..a68ae4a 100644 --- a/release-notes.md +++ b/release-notes.md @@ -13,6 +13,7 @@ Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus. - Changed behavior when team role checks fail from raising a `TeamPermissionError` to returning a 403 response, and updated affected tests. This also removes confusing stack traces from successful test runs. - Removed no longer used `TeamPermissionError` class. +- Fixed a bug where team names longer than 50 characters could cause a crash during sign up. ## Version 2024.5.2 From 116a12c4c1bb95f6e76e034ef8cd2c0b60eae131 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Wed, 22 May 2024 13:59:19 +0200 Subject: [PATCH 06/16] qr bugfix --- release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes.md b/release-notes.md index 402d80f..269d33e 100644 --- a/release-notes.md +++ b/release-notes.md @@ -14,6 +14,7 @@ Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus. and updated affected tests. This also removes confusing stack traces from successful test runs. - Removed no longer used `TeamPermissionError` class. - Fixed a bug where team names longer than 50 characters could cause a crash during sign up. +- Fixed a bug where 2fa QR codes had a dark background when dark mode was enabled. ## Version 2024.5.3 From a76578eba56f8e652ae20fed477e4de187d3873a Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Sat, 25 May 2024 16:58:25 +0200 Subject: [PATCH 07/16] more release notes --- release-notes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/release-notes.md b/release-notes.md index 269d33e..568708d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -15,6 +15,12 @@ Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus. - Removed no longer used `TeamPermissionError` class. - Fixed a bug where team names longer than 50 characters could cause a crash during sign up. - Fixed a bug where 2fa QR codes had a dark background when dark mode was enabled. +- Make Team IDs optional on the create team page (HTMX builds). +- Switch celerybeat to use the database instead of the filesystem for the scheduler, which makes it work more reliably + on docker-based systems like Kamal. (Thanks Peter and Artem for the suggestion!) +- Add display and sort on the number of active members in the teams admin. +- Fixed a bug where creating an API key crashed if your user's first + last name combined to more than 40 characters. + (Thanks Luc for reporting!) ## Version 2024.5.3 From f25d89303fb31ecfc743ca1e554a770489363aec Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 4 Jun 2024 16:16:16 +0200 Subject: [PATCH 08/16] draft release notes for 2024.6 --- release-notes.md | 98 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/release-notes.md b/release-notes.md index 568708d..91406de 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,25 +3,97 @@ Version History and Release Notes Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus.com/) are documented here. -## Next Release +## Version 2024.6 + +This is a feature release with a few bigger updates. + +### AI model changes + +Use LiteLLM instead of LLM. Docs. + +### Spam prevention updates + +Turnstile TODO docs + +### Ruff support + import removal + +TODO docs + +### Added + +- Added configurable captcha support on sign up pages, using [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/). + See updated documentation TODO +- Added API views for two-factor authentication, and to change the logged-in user's password. (Thanks Finbar for suggesting!) +- Add UI to tell users they need a verified email address prior to setting up two-factor auth. + - Also added a `has_verified_email` helper class to the `CustomUser` model. +- Added tests for the delete team view for both team admins and members. (HTMX builds only) +- Added test for team member removal permissions. + +### Fixed -- Remove `static/css` and `static/js` directories from the `dockerignore` file so that other project files - can be included. Also updated the production Docker build process so that any existing files are overwritten - by the built versions. (Thanks Raul for reporting!) -- Made some performance improvements to the production Dockerfile build - (don't rebuilding front end if there are no changes in the dependent files). -- Changed behavior when team role checks fail from raising a `TeamPermissionError` to returning a 403 response, - and updated affected tests. This also removes confusing stack traces from successful test runs. -- Removed no longer used `TeamPermissionError` class. - Fixed a bug where team names longer than 50 characters could cause a crash during sign up. -- Fixed a bug where 2fa QR codes had a dark background when dark mode was enabled. -- Make Team IDs optional on the create team page (HTMX builds). -- Switch celerybeat to use the database instead of the filesystem for the scheduler, which makes it work more reliably - on docker-based systems like Kamal. (Thanks Peter and Artem for the suggestion!) +- Fixed a bug where multi-factor authentication QR codes had a dark background when dark mode was enabled (Tailwind builds only). + (Thanks Artem for reporting!) +- Fixed a bug where it was possible to bypass two-factor-authentication when using the API authentication views. + (Thanks Finbar for reporting and helping with the fix!) - Add display and sort on the number of active members in the teams admin. +- Fixed a bug where deleting the user's only team while impersonating them resulted in a temporary crash. + (Thanks EJ for reporting!) - Fixed a bug where creating an API key crashed if your user's first + last name combined to more than 40 characters. (Thanks Luc for reporting!) + +### Changed + +- Non-OpenAI builds now use `litellm` instead of `llm`. TODO more. +- **Changed the formatter/linter from `black` and `isort` to [ruff](https://github.com/astral-sh/ruff).** + - Also addressed a handful of minor linting errors that came up as a result of this change. + Codebase linting is now substantially faster. +- Removed the `static/css` and `static/js` directories from the `dockerignore` file so that other project files + can be included. Also updated the production Docker build process so that any existing files are overwritten + by the built versions. (Thanks Raul for reporting!) +- Made some performance improvements to the production Dockerfile build (don't rebuild the front end if there are + no changes in the dependent files). +- The login API response has changed, to allow for two-factor auth prompts, and more machine-readable status fields. +- **Upgraded all Python packages to the latest versions.** +- **Upgraded all JavaScript packages to the latest versions.** +- Removed the no-longer-used `use_json_field=True` argument from wagtail `StreamField`s. +- The user dashboard no longer shows users with unconfirmed email addresses if you have set + `ACCOUNT_EMAIL_VERIFICATION = 'mandatory'`. This helps filter out likely bots from the report. +- The user dashboard now includes sign ups from the current date, by default. +- Better support trialing subscriptions with no payment methods. + The subscription UI will now show the date the trial ends and won't log errors about missing invoices. (Thanks Jarrett for reporting!) +- Changed behavior when team role checks fail from raising a `TeamPermissionError` to returning a 403 response, + and updated affected tests. One side effect of this is that the stack traces are removed from successful test runs. +- Secret keys should no longer change every time you build your Pegasus project. + They are also now clearly prefixed with `django-insecure-` to indicate that they should be changed in production. +- Updated the default OpenAI chat model to gpt-4o. +- Upgraded the openapi client generator to version 7.5.0 and also pinned the version used by `make build-api-client` + to the same one. +- **Celerybeat now uses the `django-celery-beat` library to store tasks in the database instead of on the filesystem.** + This improves support for celerybeat on Docker-based platforms. (Thanks Peter and Artem for the suggestion!) + - Also added a migration to save the default scheduled tasks in the database. +- Make Team IDs optional on the create team page (HTMX builds only). +- Add clearer error message when charts are broken due to api config issue. (Thanks Yngve for reporting!) +- Added `assume_scheme="https"` to form `URLField`s to be compatible with Django 6 behavior. +- Added `FORMS_URLFIELD_ASSUME_HTTPS = True` to be compatible with Django 6 behavior. +- Set `ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False` by default, so that "forgot password" emails do not get sent to unknown accounts. + This can help prevent spam bots. + +### Removed + +- Removed `black` and `isort` from dev-requirements, since they have been replaced by `ruff`. +- Removed `llm` library and associated code, since it has been replaced by `litellm`. +- Removed no longer used `TeamPermissionError` class. + +### Standalone front end + +The following changes affect the experimental [standalone front end](./experimental/react-front-end.md): + +- The standalone React front end now supports two-factor-authentication. +- Improve the UI when you have login issues in the standalone React front end. + + ## Version 2024.5.3 This is a hotfix release that fixes a bug where the landing and dashboard page image was accidentally From a863d791c28b38f5bb73e4290692ff511baa7c81 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 4 Jun 2024 16:53:24 +0200 Subject: [PATCH 09/16] update defaults to gpt-4o --- ai.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ai.md b/ai.md index 2ef0b8d..01e5ed2 100644 --- a/ai.md +++ b/ai.md @@ -20,7 +20,7 @@ We recommend choosing "OpenAI" unless you know you want to use a different model ### Configuring OpenAI If you're using OpenAI, you need to set `OPENAI_API_KEY` in your environment or settings file (`.env` in development). -You can also change the model used by setting `OPENAI_MODEL`, which defaults to `"gpt-3.5-turbo"`. +You can also change the model used by setting `OPENAI_MODEL`, which defaults to `"gpt-4o"`. See [this page](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key) for help finding your OpenAI API key. @@ -33,7 +33,7 @@ values in your `settings.py`. For example: ```python LLM_MODELS = { "gpt-3.5-turbo": {"api_key": env("OPENAI_API_KEY", default="")}, - "gpt4": {"api_key": env("OPENAI_API_KEY", default="")}, + "gpt-4o": {"api_key": env("OPENAI_API_KEY", default="")}, "claude-3-opus-20240229": {"api_key": env("ANTHROPIC_API_KEY", default="")}, "ollama_chat/llama3": {"api_base": env("OLLAMA_API_BASE", default="http://localhost:11434")}, # requires a running ollama instance } From 539a4ca6696c9b074cec6f7b9a2f7eb4af920f54 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 4 Jun 2024 16:53:35 +0200 Subject: [PATCH 10/16] add blurb about ollama --- release-notes.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 91406de..4824d4c 100644 --- a/release-notes.md +++ b/release-notes.md @@ -9,7 +9,16 @@ This is a feature release with a few bigger updates. ### AI model changes -Use LiteLLM instead of LLM. Docs. +The library used for non-OpenAI LLMs was changed from [`llm`](https://github.com/simonw/llm) to [`litellm`](https://docs.litellm.ai/docs/). +Reasons for this change include: + +- It has far fewer additional dependencies. +- It supports async APIs out of the box. +- The `llm` library is optimized for the command line use-case, whereas `litellm` offers similar functionality as a native + Python library. + +Litellm can still be used with all common AI models, including OpenAI, Anthropic/Claude, and Meta/Llama models +(with ollama). For details on getting started with `litellm` see the updated [AI documentation](./ai.md). ### Spam prevention updates From 97e6b5a38941ae43dfd91a934f22ed568387262d Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 4 Jun 2024 16:53:54 +0200 Subject: [PATCH 11/16] update code formatting docs --- code-structure.md | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/code-structure.md b/code-structure.md index 956fc15..27d5209 100644 --- a/code-structure.md +++ b/code-structure.md @@ -83,7 +83,9 @@ The majority of the project's base template layouts are in the `templates/web` f ## Code formatting For projects that have enabled the `Autoformat code` option, the code will have been formatted -using [black](https://black.readthedocs.io/en/stable/) and [isort](https://pycqa.github.io/isort/). +using [ruff](https://github.com/astral-sh/ruff)—a drop-in replacement for +[black](https://black.readthedocs.io/en/stable/) and [isort](https://pycqa.github.io/isort/) that runs +much faster than those tools. The project will also include [pre-commit](https://pre-commit.com/) as a dependency in the requirements file as well as the `.pre-commit-config.yaml` file in the root directory. pre-commit is a tool for managing pre-commit @@ -96,19 +98,18 @@ $ pre-commit install --install-hooks pre-commit installed at .git/hooks/pre-commit ``` -The default configuration that ships with Pegasus will run `isort` and `black` prior to every Git +The default configuration that ships with Pegasus will run `ruff` and `ruff-format` prior to every Git commit. If there are fixes that are needed you will be notified in the shell output. ### pre-commit Usage **Manually running hooks** + ```shell # run all hooks against currently staged files pre-commit run # run all the hooks against all the files. This is a useful invocation if you are using pre-commit in CI. pre-commit run --all-files -# only run the isort hook against all staged files -pre-commit run isort ``` **Temporarily disable hooks** @@ -120,27 +121,16 @@ For more information on using and configuring pre-commit check out the ### Tool configurations -The configuration for the tools can be found in the [`pyproject.toml`][toml] file. - -[toml]: https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#what-on-earth-is-a-pyproject-toml-file -**isort** +The configuration for the tools can be found in the [`pyproject.toml`][toml] file, using the same syntax as `black`. -| Parameter | Value | -|--------------|---------| -| Filter Files | `true` | -| Profile | `black` | -| Line Length | `120` | - -[iSort docs](https://pycqa.github.io/isort/docs/configuration/options) - -**black** +[toml]: https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#what-on-earth-is-a-pyproject-toml-file -| Parameter | Value | -|--------------|---------| -| Line Length | `120` | +For the most part the default black/ruff formats have been preserved, with a few updates, for example, +increasing the line length to 120. -[Black docs](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html) +You can find more information about these values in the +[ruff README](https://github.com/astral-sh/ruff?tab=readme-ov-file#configuration). ### Upgrading From 8ef7b42dbcd5af49c0e70b50c52f37016812567b Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 4 Jun 2024 16:54:55 +0200 Subject: [PATCH 12/16] add caveat --- cookbooks.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cookbooks.md b/cookbooks.md index 65c0c95..5f15c1c 100644 --- a/cookbooks.md +++ b/cookbooks.md @@ -28,6 +28,9 @@ Now you should be able to access the django admin at http://localhost:8000/admin As of February, 2023 all Pegasus projects have the option to auto-format your Python code. +As of Jun, 2024, this formatting is done by `ruff`. +This guide will be updated to use `ruff` soon, but for now is still written for `black`/`isort`. + To migrate a project from non-formatted to formatted code, you can go through the following steps: 1. First, do a full Pegasus upgrade to the version you want to update to, as described [here](./upgrading.md). From 0e061918c53186cc69fb9cf2725fdac0a13c420b Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Tue, 4 Jun 2024 16:59:09 +0200 Subject: [PATCH 13/16] rewrite config to use ruff --- cookbooks.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cookbooks.md b/cookbooks.md index 5f15c1c..0a25f7e 100644 --- a/cookbooks.md +++ b/cookbooks.md @@ -28,17 +28,14 @@ Now you should be able to access the django admin at http://localhost:8000/admin As of February, 2023 all Pegasus projects have the option to auto-format your Python code. -As of Jun, 2024, this formatting is done by `ruff`. -This guide will be updated to use `ruff` soon, but for now is still written for `black`/`isort`. - To migrate a project from non-formatted to formatted code, you can go through the following steps: 1. First, do a full Pegasus upgrade to the version you want to update to, as described [here](./upgrading.md). **Do *not* check the "autoformat" checkbox yet.** 2. Next, run the formatting tools on your project's `main` branch: - 1. Install black and isort: `pip install black isort` - 2. Run black: `black --extend-exclude migrations --line-length 120 .` - 3. Run isort: `isort -l 120 --profile black .` + 1. Install ruff: `pip install ruff` + 2. Run ruff linting `ruff check --extend-exclude migrations --line-length 120 . --fix` + 3. Run ruff formatting: `ruff format --line-length 120 .` 3. Commit the result: 1. `git add .` 2. `git commit -m "apply formatting changes"` From 1ddb12f162464fae5cf1fdebfc4d42eaaa2824c6 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Wed, 5 Jun 2024 08:58:54 +0200 Subject: [PATCH 14/16] add turnstile documentation --- configuration.md | 14 ++++++++++++++ deployment/production-checklist.md | 2 +- images/turnstile.png | Bin 0 -> 31476 bytes release-notes.md | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 images/turnstile.png diff --git a/configuration.md b/configuration.md index cb23747..2261a93 100644 --- a/configuration.md +++ b/configuration.md @@ -281,6 +281,20 @@ For help configuring LLMs and AIs, see the [AI docs](./ai.md). See the [celery docs](/celery) for set up and configuration of Celery. +## Turnstile + +To enable support for [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/), +set `TURNSTILE_KEY` and `TURNSTILE_SECRET` in your settings or environment variables. + +This should automatically enable turnstile on your sign up pages. + +It is recommended to create two different Turnstile accounts on Cloudflare for development and production. +In development you can specify "localhost" as your domain like this: + +![Turnstile Dev](/images/turnstile.png) + +In production, you should replace that with your site's production domain. + ## Mailing List diff --git a/deployment/production-checklist.md b/deployment/production-checklist.md index b31efc2..e6d9e35 100644 --- a/deployment/production-checklist.md +++ b/deployment/production-checklist.md @@ -95,7 +95,7 @@ The platform-specific docs have some guidance on setting this up where possible. ## Update other configuration options See [the configuration page](/configuration.md) for a larger list of options, -including social login, sign up flow changes, analytics, logging, and so on. +including social login, sign up flow changes, analytics, logging, adding captchas, and so on. ## Set up monitoring diff --git a/images/turnstile.png b/images/turnstile.png new file mode 100644 index 0000000000000000000000000000000000000000..b56b4dce5e3d6bb250fddfe3ac18e90938d82738 GIT binary patch literal 31476 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 bO~?dkrf1THD;;(sHICYAEu~5Y%dr0emAfZr literal 0 HcmV?d00001 diff --git a/release-notes.md b/release-notes.md index 4824d4c..a39b430 100644 --- a/release-notes.md +++ b/release-notes.md @@ -31,7 +31,7 @@ TODO docs ### Added - Added configurable captcha support on sign up pages, using [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/). - See updated documentation TODO + See [the turnstile documentation](./configuration.md#turnstile) for more information on setting this up. - Added API views for two-factor authentication, and to change the logged-in user's password. (Thanks Finbar for suggesting!) - Add UI to tell users they need a verified email address prior to setting up two-factor auth. - Also added a `has_verified_email` helper class to the `CustomUser` model. From db773b54e584bd85071ad39282683a46646b1d26 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Wed, 5 Jun 2024 10:29:17 +0200 Subject: [PATCH 15/16] final(?) release notes --- release-notes.md | 102 ++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/release-notes.md b/release-notes.md index a39b430..18fffe1 100644 --- a/release-notes.md +++ b/release-notes.md @@ -9,69 +9,98 @@ This is a feature release with a few bigger updates. ### AI model changes -The library used for non-OpenAI LLMs was changed from [`llm`](https://github.com/simonw/llm) to [`litellm`](https://docs.litellm.ai/docs/). -Reasons for this change include: +The library used for non-OpenAI LLMs has been changed from [`llm`](https://github.com/simonw/llm) +to [`litellm`](https://docs.litellm.ai/docs/). Reasons for this change include: - It has far fewer additional dependencies. -- It supports async APIs out of the box. -- The `llm` library is optimized for the command line use-case, whereas `litellm` offers similar functionality as a native - Python library. - +- It supports async APIs out of the box (for most models). +- The `llm` library is more targeted for the command line use-case, whereas `litellm` offers similar functionality + as a native Python library with a cleaner API. + Litellm can still be used with all common AI models, including OpenAI, Anthropic/Claude, and Meta/Llama models -(with ollama). For details on getting started with `litellm` see the updated [AI documentation](./ai.md). +(via ollama). For details on getting started with `litellm` see the updated [AI documentation](./ai.md). + +### Formatting and linting now use Ruff + +Black and isort have been replaced with [ruff](https://github.com/astral-sh/ruff)---a Python linter/formatter +that offers the same functionality as those tools but is much faster. + +Additionally, Pegasus will now remove unused imports from your files automatically, both +when building your project and if you have set up `pre-commit`. + +This change should be a relatively seamless drop-in replacement, though you may see some new lint errors +in your projects which you can choose to address. ### Spam prevention updates -Turnstile TODO docs +There has been a dramatic increase in spam-bots over the last month. +Many of these bots target seemingly-innocuous functionality like sign up and password reset forms. -### Ruff support + import removal +This version includes a few updates to help combat these bots. +First, you can now easily add [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/) to your sign up forms, +which will present the user with a captcha and should help reduce bot sign-ups. +See [the turnstile documentation](./configuration.md#turnstile) for information on setting this up. -TODO docs +Additionally, the `ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS` setting has been set to `False` by default. +This prevents "forgot password" and "magic link" emails from being sent out to unknown accounts. +It should also help reduce unnecessary email sending. -### Added +Finally, the [admin dashboard](#admin-dashboard) no longer shows users with unconfirmed email addresses if you have set +`ACCOUNT_EMAIL_VERIFICATION = 'mandatory'`. +This helps filter out likely bots from the report to provide clearer visibilty of people actually signing up for your app. + +### Complete changelog + +Below is the complete set of changes in this release. + +#### Added -- Added configurable captcha support on sign up pages, using [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/). +- **Added configurable captcha support on sign up pages, using [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/).** See [the turnstile documentation](./configuration.md#turnstile) for more information on setting this up. - Added API views for two-factor authentication, and to change the logged-in user's password. (Thanks Finbar for suggesting!) - Add UI to tell users they need a verified email address prior to setting up two-factor auth. - Also added a `has_verified_email` helper class to the `CustomUser` model. - Added tests for the delete team view for both team admins and members. (HTMX builds only) - Added test for team member removal permissions. +- Add display and sort on the number of active members in the teams admin. -### Fixed +#### Fixed - Fixed a bug where team names longer than 50 characters could cause a crash during sign up. - Fixed a bug where multi-factor authentication QR codes had a dark background when dark mode was enabled (Tailwind builds only). (Thanks Artem for reporting!) - Fixed a bug where it was possible to bypass two-factor-authentication when using the API authentication views. (Thanks Finbar for reporting and helping with the fix!) -- Add display and sort on the number of active members in the teams admin. - Fixed a bug where deleting the user's only team while impersonating them resulted in a temporary crash. (Thanks EJ for reporting!) - Fixed a bug where creating an API key crashed if your user's first + last name combined to more than 40 characters. (Thanks Luc for reporting!) - - -### Changed - -- Non-OpenAI builds now use `litellm` instead of `llm`. TODO more. -- **Changed the formatter/linter from `black` and `isort` to [ruff](https://github.com/astral-sh/ruff).** - - Also addressed a handful of minor linting errors that came up as a result of this change. - Codebase linting is now substantially faster. -- Removed the `static/css` and `static/js` directories from the `dockerignore` file so that other project files - can be included. Also updated the production Docker build process so that any existing files are overwritten +- Improved the UI feedback when LLMs fail (e.g. if your API key is wrong or ollama is not running). +- Removed the `static/css` and `static/js` directories from the `.dockerignore` file so that other project files + can be included in these directories. + Also updated the production Docker build process so that any existing files are overwritten by the built versions. (Thanks Raul for reporting!) - Made some performance improvements to the production Dockerfile build (don't rebuild the front end if there are no changes in the dependent files). -- The login API response has changed, to allow for two-factor auth prompts, and more machine-readable status fields. +- Better support trialing subscriptions with no payment methods. + The subscription UI will now show the date the trial ends and won't log errors about missing invoices. (Thanks Jarrett for reporting!) + +#### Changed + - **Upgraded all Python packages to the latest versions.** - **Upgraded all JavaScript packages to the latest versions.** +- **Non-OpenAI builds now use `litellm` instead of `llm`.** See above. +- **Changed the formatter/linter from `black` and `isort` to [ruff](https://github.com/astral-sh/ruff).** See above. + - Also addressed a handful of minor linting errors that came up as a result of this change. + - Codebase linting is now substantially faster. + - Unused imports are now automatically removed when building your projects. +- **Celerybeat now uses the `django-celery-beat` library to store tasks in the database instead of on the filesystem.** + This improves support for celerybeat on Docker-based platforms. (Thanks Peter and Artem for the suggestion!) + - Also added a migration to save the default scheduled tasks in the database. +- The login API response has changed, to allow for two-factor auth prompts, and more machine-readable status fields. - Removed the no-longer-used `use_json_field=True` argument from wagtail `StreamField`s. -- The user dashboard no longer shows users with unconfirmed email addresses if you have set - `ACCOUNT_EMAIL_VERIFICATION = 'mandatory'`. This helps filter out likely bots from the report. -- The user dashboard now includes sign ups from the current date, by default. -- Better support trialing subscriptions with no payment methods. - The subscription UI will now show the date the trial ends and won't log errors about missing invoices. (Thanks Jarrett for reporting!) +- The admin dashboard no longer shows users with unconfirmed email addresses if you have set `ACCOUNT_EMAIL_VERIFICATION = 'mandatory'`. +- The admin dashboard now includes sign ups from the current date, by default. - Changed behavior when team role checks fail from raising a `TeamPermissionError` to returning a 403 response, and updated affected tests. One side effect of this is that the stack traces are removed from successful test runs. - Secret keys should no longer change every time you build your Pegasus project. @@ -79,29 +108,28 @@ TODO docs - Updated the default OpenAI chat model to gpt-4o. - Upgraded the openapi client generator to version 7.5.0 and also pinned the version used by `make build-api-client` to the same one. -- **Celerybeat now uses the `django-celery-beat` library to store tasks in the database instead of on the filesystem.** - This improves support for celerybeat on Docker-based platforms. (Thanks Peter and Artem for the suggestion!) - - Also added a migration to save the default scheduled tasks in the database. -- Make Team IDs optional on the create team page (HTMX builds only). +- Team IDs are now optional on the create team page (HTMX builds only). - Add clearer error message when charts are broken due to api config issue. (Thanks Yngve for reporting!) - Added `assume_scheme="https"` to form `URLField`s to be compatible with Django 6 behavior. - Added `FORMS_URLFIELD_ASSUME_HTTPS = True` to be compatible with Django 6 behavior. - Set `ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False` by default, so that "forgot password" emails do not get sent to unknown accounts. This can help prevent spam bots. -### Removed +#### Removed - Removed `black` and `isort` from dev-requirements, since they have been replaced by `ruff`. - Removed `llm` library and associated code, since it has been replaced by `litellm`. - Removed no longer used `TeamPermissionError` class. -### Standalone front end +#### Standalone front end The following changes affect the experimental [standalone front end](./experimental/react-front-end.md): -- The standalone React front end now supports two-factor-authentication. +- **The standalone React front end now supports two-factor-authentication.** - Improve the UI when you have login issues in the standalone React front end. +*June 5, 2024* + ## Version 2024.5.3 From 4fd7a3dadb68e812a760d45b29d2e2beae5c3826 Mon Sep 17 00:00:00 2001 From: Cory Zue Date: Wed, 5 Jun 2024 10:34:52 +0200 Subject: [PATCH 16/16] tweaks --- release-notes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release-notes.md b/release-notes.md index 18fffe1..7772cab 100644 --- a/release-notes.md +++ b/release-notes.md @@ -5,7 +5,7 @@ Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus. ## Version 2024.6 -This is a feature release with a few bigger updates. +This is a feature release with a few big updates and a lot of smaller ones. ### AI model changes @@ -17,7 +17,7 @@ to [`litellm`](https://docs.litellm.ai/docs/). Reasons for this change include: - The `llm` library is more targeted for the command line use-case, whereas `litellm` offers similar functionality as a native Python library with a cleaner API. -Litellm can still be used with all common AI models, including OpenAI, Anthropic/Claude, and Meta/Llama models +Litellm can still be used with all common AI models, including OpenAI, Anthropic/Claude, and local models (via ollama). For details on getting started with `litellm` see the updated [AI documentation](./ai.md). ### Formatting and linting now use Ruff @@ -56,7 +56,7 @@ Below is the complete set of changes in this release. #### Added - **Added configurable captcha support on sign up pages, using [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/).** - See [the turnstile documentation](./configuration.md#turnstile) for more information on setting this up. + See [the turnstile documentation](./configuration.md#turnstile) for more information on setting this up. (Thanks Troy, Jacob, Robert and others for suggesting.) - Added API views for two-factor authentication, and to change the logged-in user's password. (Thanks Finbar for suggesting!) - Add UI to tell users they need a verified email address prior to setting up two-factor auth. - Also added a `has_verified_email` helper class to the `CustomUser` model. @@ -89,7 +89,7 @@ Below is the complete set of changes in this release. - **Upgraded all Python packages to the latest versions.** - **Upgraded all JavaScript packages to the latest versions.** -- **Non-OpenAI builds now use `litellm` instead of `llm`.** See above. +- **Non-OpenAI builds now use `litellm` instead of `llm`.** See above. (Thanks Sarthak for the suggestion!) - **Changed the formatter/linter from `black` and `isort` to [ruff](https://github.com/astral-sh/ruff).** See above. - Also addressed a handful of minor linting errors that came up as a result of this change. - Codebase linting is now substantially faster.