Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: CI

on:
pull_request:
push:
branches: [main, main2.0]
workflow_dispatch:

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
django:
name: Tests and checks
runs-on: ubuntu-latest
env:
PROJECT_NAME: whiteneuron.base
DJANGO_SETTINGS_MODULE: whiteneuron.base.settings
SECRET_KEY: ci-only-not-secret
DEBUG: "True"
SKIP_GUEST_LOGIN: "True"
DATABASE: sqlite
PYTHONPATH: ${{ github.workspace }}
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.13"

- name: Install dependencies
run: uv sync --frozen

- name: Install gettext
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext

- name: Django system check
working-directory: whiteneuron
run: uv run python -m django check

- name: Migrations are up to date
working-directory: whiteneuron
run: uv run python -m django makemigrations --check --dry-run

- name: Translation catalogs compile
run: |
for po in $(find whiteneuron -name django.po); do msgfmt --check -o /dev/null "$po"; done

- name: Tests
working-directory: whiteneuron
run: uv run python -m django test whiteneuron

assets:
name: Compiled CSS is up to date
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# Tailwind scans md2html_tailwind4 inside .venv (see styles.css @source)
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.13"

- name: Install Python dependencies
run: uv sync --frozen

- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm

- name: Install Node dependencies
run: npm ci

- name: Rebuild themes and CSS
run: bash scripts/tailwind.sh

- name: Committed files match the build
run: |
git diff --exit-code -- daisy-themes.css whiteneuron/base/themes.py whiteneuron/static/base/css/styles.css \
|| { echo "::error::Run 'bash scripts/tailwind.sh' and commit the regenerated files."; exit 1; }
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,4 @@ scripts/hooks/
.opencode
*/.DS_Store

AGENTS.md
AGENTS.md.tw-md2html
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
**Feature release: daisyUI multi-theme admin, server monitoring dashboard, django-unfold 0.109, security hardening and performance work**
- **Added**: 35 daisyUI themes selectable from the user menu (**Themes**); each theme also re-colors Unfold and drives its dark mode. `WN_ADMIN_THEMES` limits the picker (`[]` hides it); `WN_THEMES="nord,dracula" bash scripts/tailwind.sh` builds a smaller CSS bundle.
- **Added**: `whiteneuron.monitoring` — Grafana-style **Server monitoring** dashboard (CPU, memory, disk, load, network, disk I/O, request throughput, p50/p95/p99 latency, error rate, Apdex, slowest endpoints, DB/cache latency) with time ranges, auto-refresh, host selector and shared crosshair. Collect with `manage.py monitoring_collector` or the Celery task `whiteneuron.monitoring.tasks.collect_metrics`; configure with `WN_MONITORING`. New dependency: `psutil`.
- **Changed**: `django-unfold>=0.109.0`; template overrides ported; uses the avatar badge (unread notifications), sidebar sub-navigation (Celery "Schedules"), `SITE_VERSION`, theme-aware dashboard charts.
- **Changed**: `django-unfold>=0.109.0,<0.111` (the cap is raised after each Unfold release is re-tested against the template overrides); template overrides ported; uses the avatar badge (unread notifications), sidebar sub-navigation (Celery "Schedules"), `SITE_VERSION`, theme-aware dashboard charts.
- **Security**: escaped notification/grid HTML (stored XSS), whitelisted `?display_fields` (a GET could call `obj.delete()`) and limited the column picker to fields shown on the change form (never `password`; extra exclusions via `ModelAdmin.field_selection_exclude`), staff-only per-user notification WebSocket with origin check, sandboxed uploaded-HTML preview, private exports scoped to the selected rows, `X-Forwarded-For` read from the right (`TRUSTED_PROXY_COUNT`, `IGNORE_X_FORWARDED_FOR`), open-redirect fix in guest login, new-user signal no longer overwrites passwords, `secrets`-based passwords redacted after sending (never passed to the task broker), POST-only feedback resolve, notification IDOR fix.
- **Performance**: activity logging 1 INSERT per request (was 3–4 queries), dashboard 4 queries (was ~16) + 60s cache, one `bulk_create` + one broadcast per notification, new indexes, atomic rate-limit counters, request-scoped admin state (grid view / page size no longer shared between users), thumbnails built at upload (regenerated if the file is missing), N+1 fixes, 14KB logo instead of 187KB.
- **Fixed**: dashboard date ranges overlapped by a day, `App.__str__` crash on create, export action signature, `readonly_fields` growing per request, header horizontal overflow on phones, `_(f"…")` strings that could never be translated, Chart.js crash on oklch theme colors, notification WebSocket reconnect loops, monitoring collector stopping when the cache is down; Vietnamese translations updated.
- **Compatibility**: `ModelAdmin.request` still works (now request-scoped, not stored on the shared instance); the *Server monitoring* sidebar link follows the `monitoring.view_metricsample` permission.
- **Validation**: 35 package tests pass (also under the package's own settings via `whiteneuron.base.testing.package_test_settings`) (`manage.py test whiteneuron`), `check` clean, `makemigrations` reports no changes, Tailwind build and `uv build` succeed; changelists, add/change forms, themes and the monitoring dashboard checked in Chromium (desktop + mobile).
- **Fixed**: dashboard date ranges overlapped by a day, `App.__str__` crash on create, export action signature, `readonly_fields` growing per request, header horizontal overflow on phones, `_(f"…")` strings that could never be translated, Chart.js crash on oklch theme colors, notification WebSocket reconnect loops, monitoring collector stopping when the cache is down, missing site branding (sidebar logo/title) on the monitoring dashboard and admin index, flash messages shown twice on the admin index, changelist toolbar (filter / grid / page-size) hidden for admins without `search_fields`, "Next" enabled on the last page and Previous/Next stuck disabled when Django ignores `?all`, unreadable monitoring colors under several themes, markdown preview styles (md2html_tailwind4 classes were never compiled); Vietnamese translations updated.
- **Compatibility**: `ModelAdmin.request` still works (now request-scoped, not stored on the shared instance); the *Server monitoring* sidebar link follows the `monitoring.view_metricsample` permission; in the default light/dark modes daisyUI components take their base/primary colors from `UNFOLD["COLORS"]`.
- **Validation**: 42 package tests pass (`manage.py test whiteneuron`, also under the package's own settings via `whiteneuron.base.testing.package_test_settings`, and from a clean wheel install with django-unfold 0.110.0), `check` clean, `makemigrations` reports no changes, no untranslated Vietnamese strings, Tailwind build and `uv build` succeed; 101 admin URLs swept in a README-style project; changelists, add/change forms, the monitoring dashboard under all 35 themes and the theme picker checked in Chromium (desktop + mobile).
- **Breaking / behavior changes**:
- `SECRET_KEY` is required when `DEBUG=False` (build commands such as `collectstatic` are exempt).
- `WN_ASYNC_TASKS` (Celery for email, export, GPT translation) is opt-in; without it these run inline.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ v0.4.0.0
**Feature release: daisyUI multi-theme admin, server monitoring dashboard, django-unfold 0.109, security hardening and performance work**
- **Added**: 35 daisyUI themes selectable from the user menu (**Themes**); each theme also re-colors Unfold and drives its dark mode. `WN_ADMIN_THEMES` limits the picker (`[]` hides it); `WN_THEMES="nord,dracula" bash scripts/tailwind.sh` builds a smaller CSS bundle.
- **Added**: `whiteneuron.monitoring` — Grafana-style **Server monitoring** dashboard (CPU, memory, disk, load, network, disk I/O, request throughput, p50/p95/p99 latency, error rate, Apdex, slowest endpoints, DB/cache latency) with time ranges, auto-refresh, host selector and shared crosshair. Collect with `manage.py monitoring_collector` or the Celery task `whiteneuron.monitoring.tasks.collect_metrics`; configure with `WN_MONITORING`. New dependency: `psutil`.
- **Changed**: `django-unfold>=0.109.0`; template overrides ported; uses the avatar badge (unread notifications), sidebar sub-navigation (Celery "Schedules"), `SITE_VERSION`, theme-aware dashboard charts.
- **Changed**: `django-unfold>=0.109.0,<0.111` (the cap is raised after each Unfold release is re-tested against the template overrides); template overrides ported; uses the avatar badge (unread notifications), sidebar sub-navigation (Celery "Schedules"), `SITE_VERSION`, theme-aware dashboard charts.
- **Security**: escaped notification/grid HTML (stored XSS), whitelisted `?display_fields` (a GET could call `obj.delete()`) and limited the column picker to fields shown on the change form (never `password`; extra exclusions via `ModelAdmin.field_selection_exclude`), staff-only per-user notification WebSocket with origin check, sandboxed uploaded-HTML preview, private exports scoped to the selected rows, `X-Forwarded-For` read from the right (`TRUSTED_PROXY_COUNT`, `IGNORE_X_FORWARDED_FOR`), open-redirect fix in guest login, new-user signal no longer overwrites passwords, `secrets`-based passwords redacted after sending (never passed to the task broker), POST-only feedback resolve, notification IDOR fix.
- **Performance**: activity logging 1 INSERT per request (was 3–4 queries), dashboard 4 queries (was ~16) + 60s cache, one `bulk_create` + one broadcast per notification, new indexes, atomic rate-limit counters, request-scoped admin state (grid view / page size no longer shared between users), thumbnails built at upload (regenerated if the file is missing), N+1 fixes, 14KB logo instead of 187KB.
- **Fixed**: dashboard date ranges overlapped by a day, `App.__str__` crash on create, export action signature, `readonly_fields` growing per request, header horizontal overflow on phones, `_(f"…")` strings that could never be translated, Chart.js crash on oklch theme colors, notification WebSocket reconnect loops, monitoring collector stopping when the cache is down; Vietnamese translations updated.
- **Compatibility**: `ModelAdmin.request` still works (now request-scoped, not stored on the shared instance); the *Server monitoring* sidebar link follows the `monitoring.view_metricsample` permission.
- **Validation**: 35 package tests pass (also under the package's own settings via `whiteneuron.base.testing.package_test_settings`) (`manage.py test whiteneuron`), `check` clean, `makemigrations` reports no changes, Tailwind build and `uv build` succeed; changelists, add/change forms, themes and the monitoring dashboard checked in Chromium (desktop + mobile).
- **Fixed**: dashboard date ranges overlapped by a day, `App.__str__` crash on create, export action signature, `readonly_fields` growing per request, header horizontal overflow on phones, `_(f"…")` strings that could never be translated, Chart.js crash on oklch theme colors, notification WebSocket reconnect loops, monitoring collector stopping when the cache is down, missing site branding (sidebar logo/title) on the monitoring dashboard and admin index, flash messages shown twice on the admin index, changelist toolbar (filter / grid / page-size) hidden for admins without `search_fields`, "Next" enabled on the last page and Previous/Next stuck disabled when Django ignores `?all`, unreadable monitoring colors under several themes, markdown preview styles (md2html_tailwind4 classes were never compiled); Vietnamese translations updated.
- **Compatibility**: `ModelAdmin.request` still works (now request-scoped, not stored on the shared instance); the *Server monitoring* sidebar link follows the `monitoring.view_metricsample` permission; in the default light/dark modes daisyUI components take their base/primary colors from `UNFOLD["COLORS"]`.
- **Validation**: 42 package tests pass (`manage.py test whiteneuron`, also under the package's own settings via `whiteneuron.base.testing.package_test_settings`, and from a clean wheel install with django-unfold 0.110.0), `check` clean, `makemigrations` reports no changes, no untranslated Vietnamese strings, Tailwind build and `uv build` succeed; 101 admin URLs swept in a README-style project; changelists, add/change forms, the monitoring dashboard under all 35 themes and the theme picker checked in Chromium (desktop + mobile).
- **Breaking / behavior changes**:
- `SECRET_KEY` is required when `DEBUG=False` (build commands such as `collectstatic` are exempt).
- `WN_ASYNC_TASKS` (Celery for email, export, GPT translation) is opt-in; without it these run inline.
Expand Down
14 changes: 7 additions & 7 deletions daisy-themes.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
:root:not([data-theme]), [data-theme="light"] {
color-scheme: light;
--ui-color-base-100: oklch(100% 0 0);
--ui-color-base-200: oklch(98% 0 0);
--ui-color-base-300: oklch(95% 0 0);
--color-base-content: oklch(21% 0.006 285.885);
--ui-color-base-200: var(--color-base-50);
--ui-color-base-300: var(--color-base-100);
--color-base-content: var(--color-base-900);
--color-primary: var(--color-primary-600);
--color-primary-content: oklch(100% 0 0);
--color-secondary: oklch(65% 0.241 354.308);
Expand Down Expand Up @@ -34,10 +34,10 @@

[data-theme="dark"] {
color-scheme: dark;
--ui-color-base-100: oklch(25.33% .016 252.42);
--ui-color-base-200: oklch(23.26% .014 253.1);
--ui-color-base-300: oklch(21.15% .012 254.09);
--color-base-content: oklch(97.807% 0.029 256.847);
--ui-color-base-100: var(--color-base-900);
--ui-color-base-200: color-mix(in oklab, var(--color-base-900), var(--color-base-950));
--ui-color-base-300: var(--color-base-950);
--color-base-content: var(--color-base-100);
--color-primary: var(--color-primary-500);
--color-primary-content: oklch(100% 0 0);
--color-secondary: oklch(65% 0.241 354.308);
Expand Down
30 changes: 23 additions & 7 deletions docs/reports/releases/2026-10-13-v0.4.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ and the monitoring table).
- Chart.js could not parse oklch theme colors (converted to rgba); notification WebSocket could
open parallel reconnect loops; the monitoring collector stopped when the cache was down;
missing thumbnails were never regenerated.
- Sidebar logo/title missing on the monitoring dashboard and admin index; flash messages shown
twice on the admin index.
- Changelist toolbar (filter button, grid toggle, page size) hidden for admins without
`search_fields`; "Next" enabled on the last page; Previous/Next stuck disabled when Django
ignores `?all` (more rows than `list_max_show_all`).
- Monitoring dashboard colors unreadable under several themes (white on white in luxury, invisible
accents in black/business/cupcake); its controls are now daisyUI components.
- Markdown previews were unstyled: the classes `md2html_tailwind4` emits were never compiled (the
Tailwind `@source` used an unsupported `python3.*` directory glob).
- Closed announcement dialog made every admin page scroll sideways on phones.
- `_(f"…")` messages that could never be translated; Vietnamese catalog updated (all strings
translated).
Expand All @@ -104,12 +113,15 @@ and the monitoring table).
- Removed `EXTENSIONS.modeltranslation` and `compressed_fields` (no longer read by Unfold);
`hide_title` → `show_title`.
- `ModelAdmin.request` remains available but is request-scoped (contextvar), not shared.
- In the default light/dark modes daisyUI components (`ui-*`) take their base and primary colors
from `UNFOLD["COLORS"]` instead of daisyUI's stock palette.
- The *Server monitoring* sidebar link requires `monitoring.view_metricsample` (was
superuser only).
- Projects overriding `unfold/helpers/navigation_user.html` should compare with Unfold 0.109.
- **Schema changes**: `base.0028_perf_indexes`, `notification.0013_perf_indexes` (indexes only),
`monitoring.0001_initial` (new table).
- **New dependency**: `psutil>=7.0.0`; `django-unfold>=0.109.0`.
- **New dependency**: `psutil>=7.0.0`; `django-unfold>=0.109.0,<0.111` (capped: the package overrides
Unfold templates, so each Unfold release is re-tested before the cap is raised).
- **Python**: >=3.13 (unchanged) — **Django**: >=6.0.8,<7.0.0 (unchanged).

---
Expand Down Expand Up @@ -147,12 +159,16 @@ are dropped with their table).

## Validation

- [x] `manage.py test whiteneuron` — 35 tests pass under the test project and the package's own
settings (monitoring, regression tests for the security and correctness fixes, admin smoke
tests)
- [x] `manage.py test whiteneuron` — 42 tests pass under the test project, the package's own
settings, and a clean wheel install with django-unfold 0.110.0 (monitoring, regression
tests for the security, correctness and admin UI fixes, admin smoke tests)
- [x] `manage.py check` clean; `makemigrations` — no changes detected
- [x] Tailwind build successful (themes generated)
- [x] `msgfmt --check` / `compilemessages` for the Vietnamese catalog
- [x] Tailwind build successful and reproducible from a clean clone (themes generated); new CI
workflow (`.github/workflows/ci.yml`) runs the checks below and the CSS sync check on every PR
- [x] `msgfmt --check` / `compilemessages` for the Vietnamese catalog; `makemessages` finds no
untranslated strings
- [x] `uv build` produced wheel + sdist; wheel contains monitoring templates/static
- [x] 101 admin URLs swept in a project configured like the README (200 + site branding)
- [x] Manual checks in Chromium: all changelists and add/change forms, theme picker, dashboard,
monitoring dashboard (desktop and 390px mobile, no horizontal scroll, no JS errors)
monitoring dashboard under all 35 themes (desktop and 390px mobile, no horizontal scroll,
no JS errors)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ dependencies = [
"twisted[tls]>=26.4.0rc2",
"django-ckeditor-5>=0.2.18",
'django-cors-headers>=4.3.0',
"django-unfold>=0.109.0",
# template overrides are checked per Unfold release: raise the cap after re-testing
"django-unfold>=0.109.0,<0.111",
"djangoql>=0.18.1",
"pyopenssl>=26.4.0",
"md2html-tailwind4==1.5.3",
Expand Down
Loading
Loading