django-whiteneuron 0.3.0__tar.gz → 0.3.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. django_whiteneuron-0.3.1.1/PKG-INFO +352 -0
  2. django_whiteneuron-0.3.1.1/README.md +266 -0
  3. django_whiteneuron-0.3.1.1/django_whiteneuron.egg-info/PKG-INFO +352 -0
  4. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/django_whiteneuron.egg-info/SOURCES.txt +4 -1
  5. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/django_whiteneuron.egg-info/requires.txt +3 -2
  6. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/pyproject.toml +9 -3
  7. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/modeladmin.py +2 -0
  8. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/settings.py +13 -2
  9. django_whiteneuron-0.3.1.1/whiteneuron/base/sites.py +35 -0
  10. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/urls.py +2 -2
  11. django_whiteneuron-0.3.1.1/whiteneuron/base/utils.py +185 -0
  12. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/views.py +10 -1
  13. django_whiteneuron-0.3.1.1/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
  14. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/locale/vi/LC_MESSAGES/django.po +184 -150
  15. django_whiteneuron-0.3.1.1/whiteneuron/static/base/css/styles.css +2 -0
  16. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/js/loading.js +0 -10
  17. django_whiteneuron-0.3.0/whiteneuron/base/utils.py → django_whiteneuron-0.3.1.1/whiteneuron/templates/admin/signature.html +15 -158
  18. django_whiteneuron-0.3.1.1/whiteneuron/templates/base/announcement.html +75 -0
  19. django_whiteneuron-0.3.1.1/whiteneuron/templates/unfold/helpers/userlinks.html +46 -0
  20. django_whiteneuron-0.3.0/PKG-INFO +0 -559
  21. django_whiteneuron-0.3.0/README.md +0 -474
  22. django_whiteneuron-0.3.0/django_whiteneuron.egg-info/PKG-INFO +0 -559
  23. django_whiteneuron-0.3.0/whiteneuron/base/sites.py +0 -13
  24. django_whiteneuron-0.3.0/whiteneuron/locale/vi/LC_MESSAGES/django.mo +0 -0
  25. django_whiteneuron-0.3.0/whiteneuron/static/base/css/styles.css +0 -2
  26. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/LICENSE +0 -0
  27. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/django_whiteneuron.egg-info/dependency_links.txt +0 -0
  28. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/django_whiteneuron.egg-info/top_level.txt +0 -0
  29. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/setup.cfg +0 -0
  30. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/__init__.py +0 -0
  31. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/__init__.py +0 -0
  32. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/admin.py +0 -0
  33. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/apps.py +0 -0
  34. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/asgi.py +0 -0
  35. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/ckeditor.py +0 -0
  36. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/filters.py +0 -0
  37. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/forms.py +0 -0
  38. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/management/__init__.py +0 -0
  39. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/management/commands/__init__.py +0 -0
  40. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/management/commands/init_admin.py +0 -0
  41. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/management/commands/init_groupuser.py +0 -0
  42. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/management/commands/init_guest.py +0 -0
  43. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/middleware.py +0 -0
  44. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0001_initial.py +0 -0
  45. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0002_imagemodel_userprofile_user_avatar_user_biography_and_more.py +0 -0
  46. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0003_notification_obj_link.py +0 -0
  47. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0004_delete_notification.py +0 -0
  48. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0005_user_show_softdelete.py +0 -0
  49. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0006_user_is_bot.py +0 -0
  50. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0007_app.py +0 -0
  51. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0008_app_created_at_app_created_by_app_deleted_at_and_more.py +0 -0
  52. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0009_alter_app_table.py +0 -0
  53. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0010_app_category.py +0 -0
  54. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0011_alter_app_options.py +0 -0
  55. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0012_app_permission.py +0 -0
  56. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0013_app_thumbnail_url.py +0 -0
  57. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0014_alter_app_created_at_alter_app_updated_at_and_more.py +0 -0
  58. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0015_ipblacklist.py +0 -0
  59. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0016_uablacklist.py +0 -0
  60. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/0017_alter_uablacklist_pattern.py +0 -0
  61. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/migrations/__init__.py +0 -0
  62. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/models.py +0 -0
  63. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/resources.py +0 -0
  64. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/tasks.py +0 -0
  65. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/templatetags/__init__.py +0 -0
  66. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/templatetags/unfold_list_custom.py +0 -0
  67. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/templatetags/wn_filters.py +0 -0
  68. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/tests.py +0 -0
  69. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/thread_local.py +0 -0
  70. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/base/widgets.py +0 -0
  71. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/__init__.py +0 -0
  72. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_import_export/__init__.py +0 -0
  73. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_import_export/admin.py +0 -0
  74. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_import_export/constants.py +0 -0
  75. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_import_export/mixins.py +0 -0
  76. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_import_export/tasks.py +0 -0
  77. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_import_export/utils.py +0 -0
  78. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_modeltranslation/__init__.py +0 -0
  79. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_modeltranslation/admin.py +0 -0
  80. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/contrib/wn_modeltranslation/utils.py +0 -0
  81. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/__init__.py +0 -0
  82. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/admin.py +0 -0
  83. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/apps.py +0 -0
  84. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/migrations/__init__.py +0 -0
  85. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/models.py +0 -0
  86. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/tests.py +0 -0
  87. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/dashboard/views.py +0 -0
  88. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/__init__.py +0 -0
  89. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/admin.py +0 -0
  90. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/apps.py +0 -0
  91. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/0001_initial.py +0 -0
  92. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/0002_feedbackdata_created_by_feedbackdata_deleted_at_and_more.py +0 -0
  93. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/0003_feedbackdata_note.py +0 -0
  94. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/0004_alter_feedbackdata_object_id.py +0 -0
  95. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/0005_feedbackdata_field.py +0 -0
  96. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/0006_alter_feedbackdata_created_at_and_more.py +0 -0
  97. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/migrations/__init__.py +0 -0
  98. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/models.py +0 -0
  99. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/tests.py +0 -0
  100. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/urls.py +0 -0
  101. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/utils.py +0 -0
  102. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/feedbacks/views.py +0 -0
  103. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/__init__.py +0 -0
  104. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/admin.py +0 -0
  105. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/apps.py +0 -0
  106. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/migrations/0001_initial.py +0 -0
  107. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/migrations/0002_alter_excelfile_options_alter_pdffile_options_and_more.py +0 -0
  108. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/migrations/0003_alter_excelfile_created_at_and_more.py +0 -0
  109. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/migrations/0004_excelfile_hash_pdffile_hash.py +0 -0
  110. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/migrations/__init__.py +0 -0
  111. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/models.py +0 -0
  112. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/tests.py +0 -0
  113. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/urls.py +0 -0
  114. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/utils.py +0 -0
  115. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/file_management/views.py +0 -0
  116. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/manage.py +0 -0
  117. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/__init__.py +0 -0
  118. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/admin.py +0 -0
  119. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/apps.py +0 -0
  120. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/consumers.py +0 -0
  121. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/management/__init__.py +0 -0
  122. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/management/commands/__init__.py +0 -0
  123. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/management/commands/call_demo_noti.py +0 -0
  124. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0001_initial.py +0 -0
  125. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0002_alter_notificationconfig_event_type.py +0 -0
  126. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0003_notification.py +0 -0
  127. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0004_alter_notification_flag.py +0 -0
  128. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0005_notification_keywords.py +0 -0
  129. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0006_remove_notification_keywords.py +0 -0
  130. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0007_notification_changed_data.py +0 -0
  131. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0008_alter_notification_changed_data.py +0 -0
  132. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0009_notification_action_by_alter_notification_user.py +0 -0
  133. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0010_alter_notification_user.py +0 -0
  134. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0011_alter_notification_user.py +0 -0
  135. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/0012_alter_notification_action_and_more.py +0 -0
  136. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/migrations/__init__.py +0 -0
  137. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/models.py +0 -0
  138. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/routing.py +0 -0
  139. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/tests.py +0 -0
  140. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/utils.py +0 -0
  141. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/notification/views.py +0 -0
  142. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/css/btn-styles.css +0 -0
  143. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/css/ckeditor5.css +0 -0
  144. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/css/loading.css +0 -0
  145. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/forms/css/html-editor.css +0 -0
  146. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/forms/js/format.html.js +0 -0
  147. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/forms/js/trix.config.js +0 -0
  148. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/delete.svg +0 -0
  149. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/edit.svg +0 -0
  150. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/icon-viewlink.svg +0 -0
  151. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/loading.gif +0 -0
  152. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/login-bg.jpg +0 -0
  153. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/login_bg.jpg +0 -0
  154. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/images/logo/WhiteNeuron.png +0 -0
  155. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/js/chart.min.js +0 -0
  156. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/js/d3.v7.min.js +0 -0
  157. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/static/base/js/whiteneuron.js +0 -0
  158. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/__init__.py +0 -0
  159. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/admin.py +0 -0
  160. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/apps.py +0 -0
  161. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/migrations/__init__.py +0 -0
  162. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/models.py +0 -0
  163. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/tests.py +0 -0
  164. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/synchronization/views.py +0 -0
  165. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/400.html +0 -0
  166. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/403.html +0 -0
  167. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/404.html +0 -0
  168. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/429.html +0 -0
  169. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/500.html +0 -0
  170. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/auth/user/add_form.html +0 -0
  171. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/base/app_change_list.html +0 -0
  172. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/base/image_change_form.html +0 -0
  173. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/base/userprofile_change_form.html +0 -0
  174. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/base.html +0 -0
  175. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/change_list.html +0 -0
  176. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/feedbacks/feedback_change_form.html +0 -0
  177. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/grid_view_results.html +0 -0
  178. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/header.html +0 -0
  179. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/index.html +0 -0
  180. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/login.html +0 -0
  181. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/login_after.html +0 -0
  182. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/search_form.html +0 -0
  183. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/submit_line.html +0 -0
  184. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/wn_change_form.html +0 -0
  185. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/admin/wn_history_panel.html +0 -0
  186. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/base/forms/helpers/toolbar.html +0 -0
  187. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/base/forms/wysiwyg.html +0 -0
  188. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/base.html +0 -0
  189. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/file_management/widgets/clearable_file_input_no_download.html +0 -0
  190. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/unfold/helpers/account_links.html +0 -0
  191. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/unfold/helpers/app_list_badge.html +0 -0
  192. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/unfold/helpers/field_password.html +0 -0
  193. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/unfold/helpers/navigation_user.html +0 -0
  194. {django_whiteneuron-0.3.0 → django_whiteneuron-0.3.1.1}/whiteneuron/templates/unfold/helpers/pagination.html +0 -0
@@ -0,0 +1,352 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-whiteneuron
3
+ Version: 0.3.1.1
4
+ Summary: A modern Django Admin extension with enhanced UI/UX, dashboard, feedback, file management, rate limiting, IP blacklisting, and advanced admin integrations built on django-unfold.
5
+ Author-email: White Neuron <anhnt@whiteneuron.ai>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Nguyễn Tú Anh
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: homepage, https://whiteneuron.ai
29
+ Project-URL: repository, https://github.com/White-Neuron/django-whiteneuron
30
+ Project-URL: changelog, https://github.com/White-Neuron/django-whiteneuron/releases
31
+ Project-URL: Bug Tracker, https://github.com/White-Neuron/django-whiteneuron/issues
32
+ Project-URL: PyPI, https://pypi.org/project/django-whiteneuron/
33
+ Keywords: django,plugin,admin,dashboard,whiteneuron
34
+ Classifier: Development Status :: 4 - Beta
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Framework :: Django
37
+ Classifier: Framework :: Django :: 5.2
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: License :: OSI Approved :: MIT License
42
+ Classifier: Operating System :: OS Independent
43
+ Requires-Python: >=3.11
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Requires-Dist: django-debug-toolbar>=5.0.1
47
+ Requires-Dist: django>=5.2.13
48
+ Requires-Dist: django-celery-beat>=2.7.0
49
+ Requires-Dist: django-guardian>=2.4.0
50
+ Requires-Dist: django-imagekit>=5.0.0
51
+ Requires-Dist: django-import-export>=4.3.3
52
+ Requires-Dist: django-modeltranslation>=0.19.11
53
+ Requires-Dist: django-ninja>=1.3.0
54
+ Requires-Dist: django-simple-history>=3.8.0
55
+ Requires-Dist: gunicorn>=23.0.0
56
+ Requires-Dist: hiredis>=3.1.0
57
+ Requires-Dist: markdown>=3.7
58
+ Requires-Dist: openai>=1.58.1
59
+ Requires-Dist: openpyxl>=3.1.5
60
+ Requires-Dist: pandas>=2.2.3
61
+ Requires-Dist: pillow>=12.2.0
62
+ Requires-Dist: python-dotenv>=1.0.1
63
+ Requires-Dist: redis>=5.2.1
64
+ Requires-Dist: sentry-sdk==2.28.0
65
+ Requires-Dist: django-browser-reload>=1.18.0
66
+ Requires-Dist: whitenoise>=6.9.0
67
+ Requires-Dist: django-crispy-forms>=2.4
68
+ Requires-Dist: django-colorfield>=0.14.0
69
+ Requires-Dist: channels>=4.2.2
70
+ Requires-Dist: channels-redis>=4.2.1
71
+ Requires-Dist: uvicorn>=0.34.3
72
+ Requires-Dist: daphne>=4.2.0
73
+ Requires-Dist: django-ckeditor-5>=0.2.18
74
+ Requires-Dist: django-cors-headers>=4.3.0
75
+ Requires-Dist: django-unfold>=0.85.0
76
+ Requires-Dist: djangoql>=0.18.1
77
+ Requires-Dist: cryptography>=46.0.7
78
+ Requires-Dist: pyopenssl==26.0.0
79
+ Requires-Dist: md2html-tailwind4>=1.0.0
80
+ Provides-Extra: dev
81
+ Requires-Dist: pytest; extra == "dev"
82
+ Requires-Dist: black; extra == "dev"
83
+ Requires-Dist: flake8; extra == "dev"
84
+ Requires-Dist: pre-commit; extra == "dev"
85
+ Dynamic: license-file
86
+
87
+ # django-whiteneuron
88
+
89
+ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file management, and advanced admin integrations — built on top of [django-unfold](https://github.com/unfoldadmin/django-unfold).
90
+
91
+ [![PyPI version](https://img.shields.io/pypi/v/django-whiteneuron)](https://pypi.org/project/django-whiteneuron/)
92
+ [![Python](https://img.shields.io/pypi/pyversions/django-whiteneuron)](https://pypi.org/project/django-whiteneuron/)
93
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
94
+ [![Downloads](https://img.shields.io/pypi/dm/django-whiteneuron)](https://pypi.org/project/django-whiteneuron/)
95
+
96
+ ![django-whiteneuron](https://raw.githubusercontent.com/White-Neuron/django-whiteneuron/main2.0/docs/images/main.png)
97
+
98
+ ## Current Version
99
+
100
+ - 0.3.1.1
101
+
102
+ ## Compatibility
103
+
104
+ - Python >= 3.11
105
+ - Django >= 5.2.13
106
+ - django-unfold >= 0.85.0
107
+ - Tailwind CSS 4.x + daisyUI 5.x (for the bundled frontend styles)
108
+
109
+ ## Changelog
110
+
111
+ See [CHANGELOG.md](CHANGELOG.md) for the full version history.
112
+
113
+ ### Latest: v0.3.1.1 (2026-04-16)
114
+ **Fix: permission_viewer_callback missing unauthenticated guard; add md2html-tailwind4 dependency**
115
+ - **Fixed**: `base/utils.py` — `permission_viewer_callback` now checks `request.user.is_authenticated` before returning `True`, preventing unauthenticated (anonymous) users from passing the permission check.
116
+ - **Fixed**: `base/settings.py` — UNFOLD navigation Feedbacks menu item permission callback changed from `permission_non_guest_callback` to `permission_viewer_callback` for consistency.
117
+ - **Added**: `pyproject.toml` — added `md2html-tailwind4>=1.0.0` as a dependency.
118
+
119
+ ## Installation
120
+
121
+ ### From PyPI (recommended)
122
+
123
+ ```bash
124
+ pip install django-whiteneuron
125
+ ```
126
+
127
+ Or with [uv](https://github.com/astral-sh/uv):
128
+
129
+ ```bash
130
+ uv add django-whiteneuron
131
+ ```
132
+
133
+ ### Specific version from PyPI
134
+
135
+ ```bash
136
+ uv add "django-whiteneuron==0.2.37"
137
+ ```
138
+
139
+ ### From GitHub tag
140
+
141
+ ```bash
142
+ uv add "git+https://github.com/White-Neuron/django-whiteneuron.git@v0.2.37"
143
+ ```
144
+
145
+ ### From local source
146
+
147
+ ```bash
148
+ uv pip install -e .
149
+ ```
150
+
151
+ ## Django Configuration
152
+
153
+ Add the apps at the top of `INSTALLED_APPS`:
154
+
155
+ ```python
156
+ INSTALLED_APPS = [
157
+ "whiteneuron",
158
+ "whiteneuron.base",
159
+ "whiteneuron.feedbacks",
160
+ "whiteneuron.file_management",
161
+ "whiteneuron.contrib",
162
+ "whiteneuron.dashboard",
163
+ # ... your other apps
164
+ ]
165
+ ```
166
+
167
+ Add the required middleware **(order matters)**:
168
+
169
+ ```python
170
+ MIDDLEWARE = [
171
+ "django.middleware.security.SecurityMiddleware",
172
+ "whiteneuron.base.middleware.RateLimitMiddleware", # ← immediately after SecurityMiddleware
173
+ "whitenoise.middleware.WhiteNoiseMiddleware",
174
+ "django.contrib.sessions.middleware.SessionMiddleware",
175
+ "django.middleware.common.CommonMiddleware",
176
+ "django.middleware.csrf.CsrfViewMiddleware",
177
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
178
+ "django.contrib.messages.middleware.MessageMiddleware",
179
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
180
+ "whiteneuron.base.middleware.ReadonlyExceptionHandlerMiddleware",
181
+ "whiteneuron.base.middleware.UserActivityMiddleware", # ← after AuthenticationMiddleware
182
+ "whiteneuron.base.middleware.ThreadLocalMiddleware",
183
+ ]
184
+ ```
185
+
186
+ Set the custom user model:
187
+
188
+ ```python
189
+ AUTH_USER_MODEL = "base.User"
190
+ ```
191
+
192
+ ## UNFOLD Configuration Example
193
+
194
+ ```python
195
+ from django.templatetags.static import static
196
+ from django.utils.translation import gettext_lazy as _
197
+
198
+ UNFOLD = {
199
+ "SITE_HEADER": _("White Neuron"),
200
+ "SITE_TITLE": _("White Neuron Admin"),
201
+ "SITE_SUBHEADER": _("Admin panel"),
202
+ # Use SITE_ICON instead of SITE_LOGO to keep SITE_TITLE rendering correctly
203
+ "SITE_ICON": {
204
+ "light": lambda request: static("base/images/logo/WhiteNeuron.png"),
205
+ "dark": lambda request: static("base/images/logo/WhiteNeuron.png"),
206
+ },
207
+ "SITE_FAVICONS": [
208
+ {
209
+ "rel": "icon",
210
+ "sizes": "32x32",
211
+ "type": "image/svg+xml",
212
+ "href": lambda request: static("base/images/logo/WhiteNeuron.png"),
213
+ },
214
+ ],
215
+ "SHOW_HISTORY": False,
216
+ "SHOW_LANGUAGES": True,
217
+ "SHOW_VIEW_ON_SITE": True,
218
+ "SHOW_BACK_BUTTON": True,
219
+ "DASHBOARD_CALLBACK": "whiteneuron.dashboard.views.dashboard_callback",
220
+ "LOGIN": {
221
+ "image": lambda request: static("base/images/login-bg.jpg"),
222
+ },
223
+ "STYLES": [
224
+ lambda request: static("base/css/styles.css"),
225
+ lambda request: static("base/css/btn-styles.css"),
226
+ lambda request: static("base/css/loading.css"),
227
+ ],
228
+ "SCRIPTS": [
229
+ lambda request: static("base/js/loading.js"),
230
+ lambda request: static("base/js/whiteneuron.js"),
231
+ ],
232
+ "BORDER_RADIUS": "6px",
233
+ }
234
+ ```
235
+
236
+ ## Frontend (Tailwind 4 + daisyUI 5)
237
+
238
+ Install frontend dependencies:
239
+
240
+ ```bash
241
+ npm install -D @tailwindcss/cli@next daisyui@latest
242
+ ```
243
+
244
+ Build CSS using the provided script:
245
+
246
+ ```bash
247
+ bash scripts/tailwind.sh
248
+ ```
249
+
250
+ Or run directly:
251
+
252
+ ```bash
253
+ npx @tailwindcss/cli -i styles.css -o whiteneuron/static/base/css/styles.css --minify
254
+ ```
255
+
256
+ ## Running the Local Example
257
+
258
+ ```bash
259
+ cd whiteneuron
260
+ python manage.py migrate
261
+ python manage.py runserver
262
+ ```
263
+
264
+ Access the admin at: http://127.0.0.1:8000/admin/
265
+
266
+ ## Building the Package
267
+
268
+ ```bash
269
+ uv build
270
+ ```
271
+
272
+ Or use the all-in-one build script:
273
+
274
+ ```bash
275
+ bash scripts/build.sh
276
+ ```
277
+
278
+ ## Rate Limiting
279
+
280
+ `RateLimitMiddleware` limits by IP; `UserActivityMiddleware` limits by authenticated user. Redis is required for accuracy in multi-worker environments.
281
+
282
+ ```python
283
+ # settings.py (hoặc env)
284
+ RATE_LIMIT_REQUESTS = 60 # số request tối đa / window (theo IP)
285
+ RATE_LIMIT_WINDOW = 60 # tính bằng giây
286
+
287
+ USER_RATE_LIMIT_REQUESTS = 60 # theo user đã đăng nhập
288
+ USER_RATE_LIMIT_WINDOW = 60
289
+ ```
290
+
291
+ Corresponding env vars: `RATE_LIMIT_REQUESTS`, `RATE_LIMIT_WINDOW`, `USER_RATE_LIMIT_REQUESTS`, `USER_RATE_LIMIT_WINDOW`.
292
+
293
+ When the limit is exceeded:
294
+ - API requests (`/api/` or `Accept: application/json`) → JSON `{"detail": "Too many requests."}` with status 429.
295
+ - Browser requests → renders `429.html` template with `Retry-After` header.
296
+
297
+ ## IP Blacklist
298
+
299
+ Two protection layers operate in parallel. Both are checked before rate limiting and return 403 immediately.
300
+
301
+ ### Static — `.env` (CIDR ranges, infrastructure bans)
302
+
303
+ Loaded at startup, supports IPv4/IPv6 and CIDR:
304
+
305
+ ```env
306
+ # .env
307
+ IP_BLACKLIST=185.220.101.5,194.165.16.0/22,2001:db8::/32
308
+ ```
309
+
310
+ Requires a **Daphne/Gunicorn restart** to apply new entries.
311
+
312
+ ### Dynamic — Django Admin + Redis (real-time)
313
+
314
+ Managed via **System → IP Blacklist** in the admin panel (superuser only):
315
+
316
+ - **Permanent block**: leave `blocked_until` empty
317
+ - **Temporary block**: set `blocked_until` → Redis TTL auto-expires, no cron needed
318
+ - **Quick block from logs**: *User Activity* → select records → **Block IP address** action → Redis key set immediately, no restart needed
319
+
320
+ ```
321
+ Request → check static env blacklist (O(1))
322
+ → check cache.get('blacklist:dynamic:<ip>') (1 Redis GET)
323
+ → 403 nếu match, không tốn rate limit check
324
+ ```
325
+
326
+ ## Error Pages
327
+
328
+ Error templates live in `whiteneuron/templates/` and are used automatically by Django when `DEBUG=False`:
329
+
330
+ | Template | Error | Notes |
331
+ |---|---|---|
332
+ | `400.html` | Bad Request | |
333
+ | `403.html` | Forbidden | |
334
+ | `404.html` | Not Found | |
335
+ | `429.html` | Too Many Requests | Rendered manually by middleware, passes `{{ retry_after }}` |
336
+ | `500.html` | Server Error | |
337
+
338
+ No need to register `handler400/403/404/500` — Django resolves templates automatically via `APP_DIRS=True`.
339
+
340
+ ## Environment Configuration
341
+
342
+ Copy `env.example` to your environment file and update variables such as `DATABASE`, `REDIS`, `EMAIL`, and `ALLOWED_HOSTS`.
343
+
344
+ ## Contact
345
+
346
+ - Email: [anhnt@whiteneuron.com](mailto:anhnt@whiteneuron.com)
347
+ - Website: [https://whiteneuron.ai](https://whiteneuron.ai)
348
+ - GitHub: [https://github.com/White-Neuron/django-whiteneuron](https://github.com/White-Neuron/django-whiteneuron)
349
+
350
+ ## License
351
+
352
+ MIT License.
@@ -0,0 +1,266 @@
1
+ # django-whiteneuron
2
+
3
+ A modern Django Admin extension focused on UI/UX, dashboard, feedback, file management, and advanced admin integrations — built on top of [django-unfold](https://github.com/unfoldadmin/django-unfold).
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/django-whiteneuron)](https://pypi.org/project/django-whiteneuron/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/django-whiteneuron)](https://pypi.org/project/django-whiteneuron/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+ [![Downloads](https://img.shields.io/pypi/dm/django-whiteneuron)](https://pypi.org/project/django-whiteneuron/)
9
+
10
+ ![django-whiteneuron](https://raw.githubusercontent.com/White-Neuron/django-whiteneuron/main2.0/docs/images/main.png)
11
+
12
+ ## Current Version
13
+
14
+ - 0.3.1.1
15
+
16
+ ## Compatibility
17
+
18
+ - Python >= 3.11
19
+ - Django >= 5.2.13
20
+ - django-unfold >= 0.85.0
21
+ - Tailwind CSS 4.x + daisyUI 5.x (for the bundled frontend styles)
22
+
23
+ ## Changelog
24
+
25
+ See [CHANGELOG.md](CHANGELOG.md) for the full version history.
26
+
27
+ ### Latest: v0.3.1.1 (2026-04-16)
28
+ **Fix: permission_viewer_callback missing unauthenticated guard; add md2html-tailwind4 dependency**
29
+ - **Fixed**: `base/utils.py` — `permission_viewer_callback` now checks `request.user.is_authenticated` before returning `True`, preventing unauthenticated (anonymous) users from passing the permission check.
30
+ - **Fixed**: `base/settings.py` — UNFOLD navigation Feedbacks menu item permission callback changed from `permission_non_guest_callback` to `permission_viewer_callback` for consistency.
31
+ - **Added**: `pyproject.toml` — added `md2html-tailwind4>=1.0.0` as a dependency.
32
+
33
+ ## Installation
34
+
35
+ ### From PyPI (recommended)
36
+
37
+ ```bash
38
+ pip install django-whiteneuron
39
+ ```
40
+
41
+ Or with [uv](https://github.com/astral-sh/uv):
42
+
43
+ ```bash
44
+ uv add django-whiteneuron
45
+ ```
46
+
47
+ ### Specific version from PyPI
48
+
49
+ ```bash
50
+ uv add "django-whiteneuron==0.2.37"
51
+ ```
52
+
53
+ ### From GitHub tag
54
+
55
+ ```bash
56
+ uv add "git+https://github.com/White-Neuron/django-whiteneuron.git@v0.2.37"
57
+ ```
58
+
59
+ ### From local source
60
+
61
+ ```bash
62
+ uv pip install -e .
63
+ ```
64
+
65
+ ## Django Configuration
66
+
67
+ Add the apps at the top of `INSTALLED_APPS`:
68
+
69
+ ```python
70
+ INSTALLED_APPS = [
71
+ "whiteneuron",
72
+ "whiteneuron.base",
73
+ "whiteneuron.feedbacks",
74
+ "whiteneuron.file_management",
75
+ "whiteneuron.contrib",
76
+ "whiteneuron.dashboard",
77
+ # ... your other apps
78
+ ]
79
+ ```
80
+
81
+ Add the required middleware **(order matters)**:
82
+
83
+ ```python
84
+ MIDDLEWARE = [
85
+ "django.middleware.security.SecurityMiddleware",
86
+ "whiteneuron.base.middleware.RateLimitMiddleware", # ← immediately after SecurityMiddleware
87
+ "whitenoise.middleware.WhiteNoiseMiddleware",
88
+ "django.contrib.sessions.middleware.SessionMiddleware",
89
+ "django.middleware.common.CommonMiddleware",
90
+ "django.middleware.csrf.CsrfViewMiddleware",
91
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
92
+ "django.contrib.messages.middleware.MessageMiddleware",
93
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
94
+ "whiteneuron.base.middleware.ReadonlyExceptionHandlerMiddleware",
95
+ "whiteneuron.base.middleware.UserActivityMiddleware", # ← after AuthenticationMiddleware
96
+ "whiteneuron.base.middleware.ThreadLocalMiddleware",
97
+ ]
98
+ ```
99
+
100
+ Set the custom user model:
101
+
102
+ ```python
103
+ AUTH_USER_MODEL = "base.User"
104
+ ```
105
+
106
+ ## UNFOLD Configuration Example
107
+
108
+ ```python
109
+ from django.templatetags.static import static
110
+ from django.utils.translation import gettext_lazy as _
111
+
112
+ UNFOLD = {
113
+ "SITE_HEADER": _("White Neuron"),
114
+ "SITE_TITLE": _("White Neuron Admin"),
115
+ "SITE_SUBHEADER": _("Admin panel"),
116
+ # Use SITE_ICON instead of SITE_LOGO to keep SITE_TITLE rendering correctly
117
+ "SITE_ICON": {
118
+ "light": lambda request: static("base/images/logo/WhiteNeuron.png"),
119
+ "dark": lambda request: static("base/images/logo/WhiteNeuron.png"),
120
+ },
121
+ "SITE_FAVICONS": [
122
+ {
123
+ "rel": "icon",
124
+ "sizes": "32x32",
125
+ "type": "image/svg+xml",
126
+ "href": lambda request: static("base/images/logo/WhiteNeuron.png"),
127
+ },
128
+ ],
129
+ "SHOW_HISTORY": False,
130
+ "SHOW_LANGUAGES": True,
131
+ "SHOW_VIEW_ON_SITE": True,
132
+ "SHOW_BACK_BUTTON": True,
133
+ "DASHBOARD_CALLBACK": "whiteneuron.dashboard.views.dashboard_callback",
134
+ "LOGIN": {
135
+ "image": lambda request: static("base/images/login-bg.jpg"),
136
+ },
137
+ "STYLES": [
138
+ lambda request: static("base/css/styles.css"),
139
+ lambda request: static("base/css/btn-styles.css"),
140
+ lambda request: static("base/css/loading.css"),
141
+ ],
142
+ "SCRIPTS": [
143
+ lambda request: static("base/js/loading.js"),
144
+ lambda request: static("base/js/whiteneuron.js"),
145
+ ],
146
+ "BORDER_RADIUS": "6px",
147
+ }
148
+ ```
149
+
150
+ ## Frontend (Tailwind 4 + daisyUI 5)
151
+
152
+ Install frontend dependencies:
153
+
154
+ ```bash
155
+ npm install -D @tailwindcss/cli@next daisyui@latest
156
+ ```
157
+
158
+ Build CSS using the provided script:
159
+
160
+ ```bash
161
+ bash scripts/tailwind.sh
162
+ ```
163
+
164
+ Or run directly:
165
+
166
+ ```bash
167
+ npx @tailwindcss/cli -i styles.css -o whiteneuron/static/base/css/styles.css --minify
168
+ ```
169
+
170
+ ## Running the Local Example
171
+
172
+ ```bash
173
+ cd whiteneuron
174
+ python manage.py migrate
175
+ python manage.py runserver
176
+ ```
177
+
178
+ Access the admin at: http://127.0.0.1:8000/admin/
179
+
180
+ ## Building the Package
181
+
182
+ ```bash
183
+ uv build
184
+ ```
185
+
186
+ Or use the all-in-one build script:
187
+
188
+ ```bash
189
+ bash scripts/build.sh
190
+ ```
191
+
192
+ ## Rate Limiting
193
+
194
+ `RateLimitMiddleware` limits by IP; `UserActivityMiddleware` limits by authenticated user. Redis is required for accuracy in multi-worker environments.
195
+
196
+ ```python
197
+ # settings.py (hoặc env)
198
+ RATE_LIMIT_REQUESTS = 60 # số request tối đa / window (theo IP)
199
+ RATE_LIMIT_WINDOW = 60 # tính bằng giây
200
+
201
+ USER_RATE_LIMIT_REQUESTS = 60 # theo user đã đăng nhập
202
+ USER_RATE_LIMIT_WINDOW = 60
203
+ ```
204
+
205
+ Corresponding env vars: `RATE_LIMIT_REQUESTS`, `RATE_LIMIT_WINDOW`, `USER_RATE_LIMIT_REQUESTS`, `USER_RATE_LIMIT_WINDOW`.
206
+
207
+ When the limit is exceeded:
208
+ - API requests (`/api/` or `Accept: application/json`) → JSON `{"detail": "Too many requests."}` with status 429.
209
+ - Browser requests → renders `429.html` template with `Retry-After` header.
210
+
211
+ ## IP Blacklist
212
+
213
+ Two protection layers operate in parallel. Both are checked before rate limiting and return 403 immediately.
214
+
215
+ ### Static — `.env` (CIDR ranges, infrastructure bans)
216
+
217
+ Loaded at startup, supports IPv4/IPv6 and CIDR:
218
+
219
+ ```env
220
+ # .env
221
+ IP_BLACKLIST=185.220.101.5,194.165.16.0/22,2001:db8::/32
222
+ ```
223
+
224
+ Requires a **Daphne/Gunicorn restart** to apply new entries.
225
+
226
+ ### Dynamic — Django Admin + Redis (real-time)
227
+
228
+ Managed via **System → IP Blacklist** in the admin panel (superuser only):
229
+
230
+ - **Permanent block**: leave `blocked_until` empty
231
+ - **Temporary block**: set `blocked_until` → Redis TTL auto-expires, no cron needed
232
+ - **Quick block from logs**: *User Activity* → select records → **Block IP address** action → Redis key set immediately, no restart needed
233
+
234
+ ```
235
+ Request → check static env blacklist (O(1))
236
+ → check cache.get('blacklist:dynamic:<ip>') (1 Redis GET)
237
+ → 403 nếu match, không tốn rate limit check
238
+ ```
239
+
240
+ ## Error Pages
241
+
242
+ Error templates live in `whiteneuron/templates/` and are used automatically by Django when `DEBUG=False`:
243
+
244
+ | Template | Error | Notes |
245
+ |---|---|---|
246
+ | `400.html` | Bad Request | |
247
+ | `403.html` | Forbidden | |
248
+ | `404.html` | Not Found | |
249
+ | `429.html` | Too Many Requests | Rendered manually by middleware, passes `{{ retry_after }}` |
250
+ | `500.html` | Server Error | |
251
+
252
+ No need to register `handler400/403/404/500` — Django resolves templates automatically via `APP_DIRS=True`.
253
+
254
+ ## Environment Configuration
255
+
256
+ Copy `env.example` to your environment file and update variables such as `DATABASE`, `REDIS`, `EMAIL`, and `ALLOWED_HOSTS`.
257
+
258
+ ## Contact
259
+
260
+ - Email: [anhnt@whiteneuron.com](mailto:anhnt@whiteneuron.com)
261
+ - Website: [https://whiteneuron.ai](https://whiteneuron.ai)
262
+ - GitHub: [https://github.com/White-Neuron/django-whiteneuron](https://github.com/White-Neuron/django-whiteneuron)
263
+
264
+ ## License
265
+
266
+ MIT License.