Skip to content

Commit

Permalink
Admin UI tweaks (#5)
Browse files Browse the repository at this point in the history
* refactor register form to be simpler

* refactor user_create flow

* add hover effect to stat

* Refactor .env, templates and config variables

- Added APP_NAME variable in .env.ci and used it in app.py.
- Removed redundant code and reorganized user_list.html templates.
- Renamed 'user_list_view.html' to 'user_list_row.html' for consistency.
- Updated TODO.md to reflect completed tasks.

* setup sending emails via mailgun

* increment project version, and release on pr to main

* fix modal delete confirm

* add comments to lazy loading of user list

* upgrade email templates

* enable preview envs in render via blueprint

* set preview expiration to 1 day

* add email config to .env.ci

* fix webui test

---------

Co-authored-by: phernandez <[email protected]>
  • Loading branch information
phernandez and phernandez authored Sep 27, 2024
1 parent 362ecae commit 9a29fcb
Show file tree
Hide file tree
Showing 38 changed files with 1,333 additions and 1,100 deletions.
8 changes: 8 additions & 0 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[bumpversion]
current_version = 0.1.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"
2 changes: 2 additions & 0 deletions .env.ci
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
APP_NAME=Basic Foundation

POSTGRES_USER="postgres"
POSTGRES_PASSWORD="password"
POSTGRES_DB="api"
Expand Down
63 changes: 63 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Release on Merge to Main

on:
push:
branches:
- main

jobs:
release:
runs-on: ubuntu-latest

steps:
# Step 1: Checkout code
- uses: actions/checkout@v4

# Step 2: Set up Python
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

# Step 3: Install dependencies (including bump2version)
- name: Install Dependencies
run: |
pip install poetry
poetry install
pip install bump2version
# Step 4: Bump the version (patch, minor, or major)
- name: Bump Version
run: |
bump2version patch
# Step 5: Build the project
- name: Build Project
run: |
poetry build
# Step 6: Push changes (committed by bump2version)
- name: Push Version Bump Changes
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git push origin main
# Step 7: Create a GitHub Release
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: 'v${{ steps.bump_version.outputs.new_version }}' # New version from bump2version
release_name: 'Release v${{ steps.bump_version.outputs.new_version }}'
draft: false
prerelease: false

# # Step 8: Upload build artifacts to the release (optional)
# - name: Upload Build Artifacts
# uses: actions/upload-artifact@v4
# with:
# name: basic-foundation-build
# path: dist/ # Path to your build artifacts
20 changes: 10 additions & 10 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,31 @@

## todo

- [ ] package as separate modules?
- [x] package as separate modules?
- core
- api
- web-htmx
- [ ] uv
- [-] uv
- [ ] permission checks
- [x] has to be at least one admin
- [x] only admins can create/edit users
- [ ] dashboard stats should link to filtered list
- [ ] user service with current user
- [ ] make active/admin colums enums
- [ ] db: rename is_superuser to is_admin
- [ ] page_size is appended to url
- [-] page_size is appended to url
- [x] use fixtures in web tests
- [x] delete user after test
- [x] add playwright to github ci
- [x] code coverage for ci
- [ ] Api docs say FASTAPI
- [x] Api docs say FASTAPI
- [ ] responsive tests for playwright
- [x] pywright
- [ ] form includes
- [ ] users page refresh
- [ ] mailapi - sendgrid? or mailgun?
- https://sabuhish.github.io/fastapi-mail/example/
- https://pramod4040.medium.com/fastapi-forget-password-api-setup-632ab90ba958
- [x] form includes
- [x] users page refresh
- [ ] mailapi
- mailgun
- setup domain
- mx record
- [ ] automate github releases on merge to main

## feature
Expand Down
5 changes: 3 additions & 2 deletions foundation/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
logger.remove()
logger.add(sys.stderr, colorize=True, backtrace=True, diagnose=True)

app = FastAPI()
app = FastAPI(title=settings.APP_NAME)

# Add middleware for sessions and CSRF protection
app.add_middleware(SessionMiddleware, secret_key=settings.JWT_SECRET)
Expand All @@ -43,7 +43,8 @@ async def on_startup(): # pragma: no cover
:return: None
"""
logger.info(f"Welcome to {config.settings.app_name}")
logger.info(f"Welcome to {config.settings.APP_NAME}")
logger.info(f"email enabled: {config.settings.EMAIL_ENABLED}")

# silence bcrypt noise
logging.getLogger("passlib").setLevel(logging.ERROR)
Expand Down
2 changes: 1 addition & 1 deletion foundation/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Settings(BaseSettings):
DOMAIN: str = "localhost"
ENVIRONMENT: Literal["local", "staging", "production"] = "local"

app_name: str = "Basic API"
APP_NAME: str
JWT_SECRET: str
CSRF_SECRET: str
POSTGRES_USER: str
Expand Down
26 changes: 13 additions & 13 deletions foundation/core/emails.py → foundation/core/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class EmailData:

def render_email_template(*, template_name: str, context: dict[str, Any]) -> str:
template_str = (
BASE_DIR / "foundation" / "templates" / "email" / "build" / template_name
BASE_DIR / "foundation" / "templates" / "email" / "build" / template_name
).read_text()
html_content = Template(template_str).render(context)
return html_content
Expand All @@ -27,8 +27,8 @@ def render_email_template(*, template_name: str, context: dict[str, Any]) -> str
def send_email( # pragma: no cover
*,
email_to: str,
subject: str = "",
html_content: str = "",
subject: str,
html_content: str,
) -> SMTPResponse:
assert settings.emails_enabled, "no provided configuration for email variables"
message = emails.Message(
Expand All @@ -47,28 +47,28 @@ def send_email( # pragma: no cover
if settings.EMAIL_SMTP_PASSWORD:
smtp_options["password"] = settings.EMAIL_SMTP_PASSWORD
response = message.send(to=email_to, smtp=smtp_options)
logger.info(f"send email result: {response}")
logger.info(f"send email {email_to} result: {response.__dict__}")
return response


def generate_test_email(email_to: str) -> EmailData:
project_name = settings.app_name
subject = f"{project_name} - Test email"
app_name = settings.APP_NAME
subject = f"{app_name} - Test email"
html_content = render_email_template(
template_name="test_email.html",
context={"project_name": project_name, "email": email_to},
context={"app_name": app_name, "email": email_to},
)
return EmailData(html_content=html_content, subject=subject)


def generate_reset_password_email(email_to: str, email: str, token: str) -> EmailData:
project_name = settings.app_name
subject = f"{project_name} - Password recovery for user {email}"
app_name = settings.APP_NAME
subject = f"{app_name} - Password recovery for user {email}"
link = f"{settings.server_host}/reset-password?token={token}"
html_content = render_email_template(
template_name="reset_password.html",
context={
"project_name": project_name,
"app_name": app_name,
"username": email,
"email": email_to,
"valid_hours": settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS,
Expand All @@ -82,12 +82,12 @@ def generate_reset_password_email(email_to: str, email: str, token: str) -> Emai
def generate_new_account_email(
email_to: str, username: str, password: str
) -> EmailData:
project_name = settings.app_name
subject = f"{project_name} - New account for user {username}"
app_name = settings.APP_NAME
subject = f"{app_name} - New account for user {username}"
html_content = render_email_template(
template_name="new_account.html",
context={
"project_name": settings.app_name,
"app_name": settings.APP_NAME,
"username": username,
"password": password,
"email": email_to,
Expand Down
2 changes: 1 addition & 1 deletion foundation/core/test/test_emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from fastapi_jwt.jwt_backends.abstract_backend import BackendException
from jwt import InvalidTokenError

from foundation.core.emails import (
from foundation.core.email import (
generate_test_email,
send_email,
generate_reset_password_email,
Expand Down
Loading

0 comments on commit 9a29fcb

Please sign in to comment.