brilliance-admin 0.39.0__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 (111) hide show
  1. brilliance_admin-0.39.0/.configs/docker/Dockerfile +22 -0
  2. brilliance_admin-0.39.0/.configs/docker/docker-compose.yml +35 -0
  3. brilliance_admin-0.39.0/.configs/nginx/example.conf +25 -0
  4. brilliance_admin-0.39.0/.env +0 -0
  5. brilliance_admin-0.39.0/.github/workflows/certbot.yml +27 -0
  6. brilliance_admin-0.39.0/.github/workflows/deploy.yml +127 -0
  7. brilliance_admin-0.39.0/.github/workflows/install-docker.yml +26 -0
  8. brilliance_admin-0.39.0/.gitignore +11 -0
  9. brilliance_admin-0.39.0/.isort.cfg +7 -0
  10. brilliance_admin-0.39.0/.python-version +1 -0
  11. brilliance_admin-0.39.0/LICENSE +17 -0
  12. brilliance_admin-0.39.0/PKG-INFO +76 -0
  13. brilliance_admin-0.39.0/README.md +46 -0
  14. brilliance_admin-0.39.0/admin_panel/__init__.py +4 -0
  15. brilliance_admin-0.39.0/admin_panel/api/__init__.py +0 -0
  16. brilliance_admin-0.39.0/admin_panel/api/routers.py +18 -0
  17. brilliance_admin-0.39.0/admin_panel/api/utils.py +28 -0
  18. brilliance_admin-0.39.0/admin_panel/api/views/__init__.py +0 -0
  19. brilliance_admin-0.39.0/admin_panel/api/views/auth.py +29 -0
  20. brilliance_admin-0.39.0/admin_panel/api/views/autocomplete.py +33 -0
  21. brilliance_admin-0.39.0/admin_panel/api/views/graphs.py +30 -0
  22. brilliance_admin-0.39.0/admin_panel/api/views/index.py +38 -0
  23. brilliance_admin-0.39.0/admin_panel/api/views/schema.py +29 -0
  24. brilliance_admin-0.39.0/admin_panel/api/views/settings.py +29 -0
  25. brilliance_admin-0.39.0/admin_panel/api/views/table.py +136 -0
  26. brilliance_admin-0.39.0/admin_panel/auth.py +32 -0
  27. brilliance_admin-0.39.0/admin_panel/docs.py +37 -0
  28. brilliance_admin-0.39.0/admin_panel/exceptions.py +38 -0
  29. brilliance_admin-0.39.0/admin_panel/integrations/__init__.py +0 -0
  30. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/__init__.py +6 -0
  31. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/auth.py +144 -0
  32. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/autocomplete.py +38 -0
  33. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/fields.py +254 -0
  34. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/fields_schema.py +316 -0
  35. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/__init__.py +19 -0
  36. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/base.py +141 -0
  37. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/create.py +73 -0
  38. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/delete.py +18 -0
  39. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/list.py +178 -0
  40. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/retrieve.py +61 -0
  41. brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/update.py +95 -0
  42. brilliance_admin-0.39.0/admin_panel/schema/__init__.py +7 -0
  43. brilliance_admin-0.39.0/admin_panel/schema/admin_schema.py +185 -0
  44. brilliance_admin-0.39.0/admin_panel/schema/category.py +149 -0
  45. brilliance_admin-0.39.0/admin_panel/schema/graphs/__init__.py +1 -0
  46. brilliance_admin-0.39.0/admin_panel/schema/graphs/category_graphs.py +50 -0
  47. brilliance_admin-0.39.0/admin_panel/schema/group.py +67 -0
  48. brilliance_admin-0.39.0/admin_panel/schema/table/__init__.py +8 -0
  49. brilliance_admin-0.39.0/admin_panel/schema/table/admin_action.py +76 -0
  50. brilliance_admin-0.39.0/admin_panel/schema/table/category_table.py +175 -0
  51. brilliance_admin-0.39.0/admin_panel/schema/table/fields/__init__.py +5 -0
  52. brilliance_admin-0.39.0/admin_panel/schema/table/fields/base.py +249 -0
  53. brilliance_admin-0.39.0/admin_panel/schema/table/fields/function_field.py +65 -0
  54. brilliance_admin-0.39.0/admin_panel/schema/table/fields_schema.py +216 -0
  55. brilliance_admin-0.39.0/admin_panel/schema/table/table_models.py +53 -0
  56. brilliance_admin-0.39.0/admin_panel/static/favicon.ico +0 -0
  57. brilliance_admin-0.39.0/admin_panel/static/index-BrXRRuaE.css +8 -0
  58. brilliance_admin-0.39.0/admin_panel/static/index-SCeDXvci.js +525 -0
  59. brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
  60. brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
  61. brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
  62. brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
  63. brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-first/content.min.css +250 -0
  64. brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-first/skin.min.css +2820 -0
  65. brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-slim/content.min.css +249 -0
  66. brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-slim/skin.min.css +2821 -0
  67. brilliance_admin-0.39.0/admin_panel/static/tinymce/img/example.png +0 -0
  68. brilliance_admin-0.39.0/admin_panel/static/tinymce/img/tinymce.woff2 +0 -0
  69. brilliance_admin-0.39.0/admin_panel/static/tinymce/lightgray/content.min.css +1 -0
  70. brilliance_admin-0.39.0/admin_panel/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
  71. brilliance_admin-0.39.0/admin_panel/static/tinymce/lightgray/skin.min.css +1 -0
  72. brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/accordion/css/accordion.css +17 -0
  73. brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/accordion/plugin.js +48 -0
  74. brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/codesample/css/prism.css +1 -0
  75. brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/customLink/css/link.css +3 -0
  76. brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/customLink/plugin.js +147 -0
  77. brilliance_admin-0.39.0/admin_panel/static/tinymce/tinymce.min.js +2 -0
  78. brilliance_admin-0.39.0/admin_panel/static/vanilla-picker-B6E6ObS_.js +8 -0
  79. brilliance_admin-0.39.0/admin_panel/templates/index.html +25 -0
  80. brilliance_admin-0.39.0/admin_panel/translations.py +145 -0
  81. brilliance_admin-0.39.0/admin_panel/utils.py +50 -0
  82. brilliance_admin-0.39.0/brilliance_admin.egg-info/PKG-INFO +76 -0
  83. brilliance_admin-0.39.0/brilliance_admin.egg-info/SOURCES.txt +109 -0
  84. brilliance_admin-0.39.0/brilliance_admin.egg-info/dependency_links.txt +1 -0
  85. brilliance_admin-0.39.0/brilliance_admin.egg-info/requires.txt +23 -0
  86. brilliance_admin-0.39.0/brilliance_admin.egg-info/top_level.txt +1 -0
  87. brilliance_admin-0.39.0/example/__init__.py +0 -0
  88. brilliance_admin-0.39.0/example/main.py +142 -0
  89. brilliance_admin-0.39.0/example/phrases.py +112 -0
  90. brilliance_admin-0.39.0/example/sections/__init__.py +0 -0
  91. brilliance_admin-0.39.0/example/sections/currency.py +20 -0
  92. brilliance_admin-0.39.0/example/sections/graphs.py +136 -0
  93. brilliance_admin-0.39.0/example/sections/merchant.py +45 -0
  94. brilliance_admin-0.39.0/example/sections/models.py +194 -0
  95. brilliance_admin-0.39.0/example/sections/payments.py +199 -0
  96. brilliance_admin-0.39.0/example/sections/terminal.py +41 -0
  97. brilliance_admin-0.39.0/example/sections/users.py +25 -0
  98. brilliance_admin-0.39.0/example/sqlite.py +45 -0
  99. brilliance_admin-0.39.0/example/utils.py +26 -0
  100. brilliance_admin-0.39.0/pyproject.toml +47 -0
  101. brilliance_admin-0.39.0/setup.cfg +4 -0
  102. brilliance_admin-0.39.0/tests/__init__.py +0 -0
  103. brilliance_admin-0.39.0/tests/conftest.py +12 -0
  104. brilliance_admin-0.39.0/tests/test_action.py +26 -0
  105. brilliance_admin-0.39.0/tests/test_payments_fields_schema.py +222 -0
  106. brilliance_admin-0.39.0/tests/test_sqlalcmeny_auth.py +98 -0
  107. brilliance_admin-0.39.0/tests/test_sqlalcmeny_crud.py +275 -0
  108. brilliance_admin-0.39.0/tests/test_sqlalcmeny_filters.py +229 -0
  109. brilliance_admin-0.39.0/tests/test_sqlalcmeny_schema.py +159 -0
  110. brilliance_admin-0.39.0/tests/test_translations.py +90 -0
  111. brilliance_admin-0.39.0/uv.lock +773 -0
@@ -0,0 +1,22 @@
1
+ FROM python:3.12-slim
2
+
3
+ # The installer requires curl (and certificates) to download the release archive
4
+ RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates
5
+
6
+ # Download the latest installer
7
+ ADD https://astral.sh/uv/install.sh /uv-installer.sh
8
+
9
+ # Run the installer then remove it
10
+ RUN sh /uv-installer.sh && rm /uv-installer.sh
11
+
12
+ # Ensure the installed binary is on the `PATH`
13
+ ENV PATH="/root/.local/bin/:$PATH"
14
+
15
+ # Copy the project into the image
16
+ ADD . /app
17
+
18
+ RUN apt-get install -y --no-install-recommends curl build-essential gettext libpq-dev git ssh openssh-client
19
+
20
+ # Sync the project into a new environment, using the frozen lockfile
21
+ WORKDIR /app
22
+ RUN uv sync --frozen
@@ -0,0 +1,35 @@
1
+ name: brilliance
2
+
3
+ volumes:
4
+ db_data:
5
+
6
+ services:
7
+ backend:
8
+ build:
9
+ context: ./../..
10
+ dockerfile: .configs/docker/Dockerfile
11
+ volumes:
12
+ - ./../../:/app
13
+ - ~/.ssh/id_rsa:/root/.ssh/id_rsa:delegated
14
+ - ~/.ssh/known_hosts:/root/.ssh/known_hosts:delegated
15
+ env_file:
16
+ - ./../../.env
17
+ tmpfs:
18
+ - /tmp
19
+ ports:
20
+ - 8082:8082
21
+ command: uv run uvicorn example.main:app --host 0.0.0.0 --port 8082 --reload --proxy-headers --forwarded-allow-ips='*'
22
+ healthcheck:
23
+ test: ['CMD', 'curl', '-f', 'http://127.0.0.1:8082']
24
+ interval: 5s
25
+ timeout: 2s
26
+ retries: 5
27
+
28
+ postgres:
29
+ image: postgres:alpine
30
+ env_file:
31
+ - ./../../.env
32
+ volumes:
33
+ - db_data:/var/lib/postgresql/data
34
+ ports:
35
+ - 5432:5432
@@ -0,0 +1,25 @@
1
+ server {
2
+ listen 80;
3
+ server_name brilliance-admin.com www.brilliance-admin.com;
4
+
5
+ return 301 https://$host$request_uri;
6
+ }
7
+
8
+ server {
9
+ listen 443 ssl;
10
+ server_name brilliance-admin.com www.brilliance-admin.com;
11
+
12
+ ssl_certificate /etc/letsencrypt/live/brilliance-admin.com/fullchain.pem;
13
+ ssl_certificate_key /etc/letsencrypt/live/brilliance-admin.com/privkey.pem;
14
+
15
+ ssl_protocols TLSv1.2 TLSv1.3;
16
+ ssl_prefer_server_ciphers off;
17
+
18
+ location / {
19
+ proxy_pass http://127.0.0.1:8082;
20
+ proxy_set_header Host $host;
21
+ proxy_set_header X-Real-IP $remote_addr;
22
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
23
+ proxy_set_header X-Forwarded-Proto https;
24
+ }
25
+ }
File without changes
@@ -0,0 +1,27 @@
1
+ name: Setup HTTPS
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ setup-https:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Setup HTTPS with certbot
11
+ uses: appleboy/ssh-action@master
12
+ with:
13
+ host: ${{ vars.SERVER_HOST }}
14
+ username: deploy
15
+ key: ${{ secrets.SERVER_SSH_KEY }}
16
+ script: |
17
+ if ! command -v certbot >/dev/null 2>&1; then
18
+ sudo apt update
19
+ sudo apt install -y certbot python3-certbot-nginx
20
+ fi
21
+ sudo certbot --nginx \
22
+ -d brilliance-admin.com \
23
+ -d www.brilliance-admin.com \
24
+ --non-interactive \
25
+ --agree-tos \
26
+ -m admin@brilliance-admin.com \
27
+ --redirect
@@ -0,0 +1,127 @@
1
+ name: CI-CD Pipeline
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+
7
+ jobs:
8
+ build-test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout code
12
+ uses: actions/checkout@v5
13
+
14
+ - name: Build containers
15
+ run: docker compose -f .configs/docker/docker-compose.yml build
16
+
17
+ - name: Run tests
18
+ run: docker compose -f .configs/docker/docker-compose.yml run --rm backend sh -c 'uv sync --all-groups --all-extras && uv run pytest'
19
+
20
+ deploy:
21
+ needs: build-test
22
+ runs-on: ubuntu-latest
23
+ if: ${{ success() }}
24
+ steps:
25
+ - name: Checkout code
26
+ uses: actions/checkout@v5
27
+
28
+ - name: Clone repo via GitHub App
29
+ uses: GuillaumeFalourd/clone-github-repo-action@v2
30
+ with:
31
+ owner: brilliance-admin
32
+ repository: backend-python
33
+ app_id: ${{ secrets.GH_APP_ID }}
34
+ installation_id: ${{ secrets.GH_APP_INSTALLATION_ID }}
35
+ private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
36
+
37
+ - name: Ensure rsync installed on server
38
+ uses: appleboy/ssh-action@master
39
+ with:
40
+ host: ${{ vars.SERVER_HOST }}
41
+ username: deploy
42
+ key: ${{ secrets.SERVER_SSH_KEY }}
43
+ script: |
44
+ if ! command -v rsync >/dev/null 2>&1; then
45
+ sudo apt update
46
+ sudo apt install -y rsync
47
+ fi
48
+
49
+ - name: Fix folder permissions
50
+ uses: appleboy/ssh-action@master
51
+ with:
52
+ host: ${{ vars.SERVER_HOST }}
53
+ username: deploy
54
+ key: ${{ secrets.SERVER_SSH_KEY }}
55
+ script: |
56
+ sudo chown -R deploy:deploy /home/deploy/apps/backend-python
57
+
58
+ - name: Copy files to server
59
+ uses: burnett01/rsync-deployments@v8
60
+ with:
61
+ switches: -avzr --delete --chown=deploy:deploy
62
+ path: .
63
+ remote_path: /home/deploy/apps/backend-python
64
+ remote_host: ${{ vars.SERVER_HOST }}
65
+ remote_user: deploy
66
+ remote_key: ${{ secrets.SERVER_SSH_KEY }}
67
+
68
+ - name: Run Docker on server
69
+ uses: appleboy/ssh-action@master
70
+ with:
71
+ host: ${{ vars.SERVER_HOST }}
72
+ username: deploy
73
+ key: ${{ secrets.SERVER_SSH_KEY }}
74
+ script: |
75
+ cd /home/deploy/apps/backend-python
76
+ docker compose -f .configs/docker/docker-compose.yml run --rm backend /bin/bash -c "uv sync --all-groups --all-extras"
77
+ docker compose -f .configs/docker/docker-compose.yml up -d --build
78
+
79
+ - name: Wait backend healthy
80
+ uses: appleboy/ssh-action@master
81
+ with:
82
+ host: ${{ vars.SERVER_HOST }}
83
+ username: deploy
84
+ key: ${{ secrets.SERVER_SSH_KEY }}
85
+ script: |
86
+ for i in 1 2 3 4 5 6; do
87
+ STATUS=$(docker inspect --format='{{.State.Health.Status}}' brilliance-backend-1 2>/dev/null || true)
88
+ if [ "$STATUS" = "healthy" ]; then
89
+ exit 0
90
+ fi
91
+ sleep 3
92
+ done
93
+ exit 1
94
+
95
+ update-nginx:
96
+ needs: deploy
97
+ runs-on: ubuntu-latest
98
+ if: ${{ success() }}
99
+ steps:
100
+
101
+ - name: Ensure nginx is running
102
+ uses: appleboy/ssh-action@master
103
+ with:
104
+ host: ${{ vars.SERVER_HOST }}
105
+ username: deploy
106
+ key: ${{ secrets.SERVER_SSH_KEY }}
107
+ script: |
108
+ if ! systemctl is-active --quiet nginx; then
109
+ sudo systemctl start nginx
110
+ fi
111
+ sudo systemctl status nginx --no-pager
112
+
113
+ - name: Ensure nginx and update config
114
+ uses: appleboy/ssh-action@master
115
+ with:
116
+ host: ${{ vars.SERVER_HOST }}
117
+ username: deploy
118
+ key: ${{ secrets.SERVER_SSH_KEY }}
119
+ script: |
120
+ if ! command -v nginx >/dev/null 2>&1; then
121
+ sudo apt update
122
+ sudo apt install -y nginx
123
+ fi
124
+ sudo ln -sf /home/deploy/apps/backend-python/.configs/nginx/example.conf /etc/nginx/sites-enabled/example
125
+ sudo rm -f /etc/nginx/sites-enabled/default
126
+ sudo nginx -t
127
+ sudo systemctl reload nginx
@@ -0,0 +1,26 @@
1
+ name: Install Docker on server
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ install-docker:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Ensure Docker installed and deploy in docker group
11
+ uses: appleboy/ssh-action@master
12
+ with:
13
+ host: ${{ vars.SERVER_HOST }}
14
+ username: ${{ vars.SERVER_USER }}
15
+ key: ${{ secrets.SERVER_SSH_KEY }}
16
+ script: |
17
+ if ! command -v docker >/dev/null 2>&1; then
18
+ sudo apt update
19
+ sudo apt install -y ca-certificates curl gnupg
20
+ sudo install -m 0755 -d /etc/apt/keyrings
21
+ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
22
+ sudo chmod a+r /etc/apt/keyrings/docker.gpg
23
+ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
24
+ sudo apt update
25
+ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
26
+ fi
@@ -0,0 +1,11 @@
1
+ # Python-generated files
2
+ test.db
3
+ __pycache__/
4
+ *.py[oc]
5
+ build/
6
+ dist/
7
+ wheels/
8
+ *.egg-info
9
+
10
+ # Virtual environments
11
+ .venv
@@ -0,0 +1,7 @@
1
+ [isort]
2
+ line_length=120
3
+ py_version=39
4
+ multi_line_output=4
5
+ known_third_party=fastapi
6
+ known_first_party=app,tests
7
+ virtual_env=py3
@@ -0,0 +1 @@
1
+ 3.10
@@ -0,0 +1,17 @@
1
+ GNU AFFERO GENERAL PUBLIC LICENSE
2
+ Version 3, 19 November 2007
3
+
4
+ Copyright (C) 2025 Your Name
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Affero General Public License as published
8
+ by the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Affero General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Affero General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: brilliance-admin
3
+ Version: 0.39.0
4
+ Summary: General-purpose admin panel backend
5
+ License-Expression: AGPL-3.0
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: asgiref>=3.11.0
10
+ Requires-Dist: fastapi>=0.115.8
11
+ Requires-Dist: jinja2>=3.1.6
12
+ Provides-Extra: example
13
+ Requires-Dist: uvicorn>=0.34.0; extra == "example"
14
+ Requires-Dist: faker>=38.2.0; extra == "example"
15
+ Requires-Dist: pyjwt>=2.10.1; extra == "example"
16
+ Requires-Dist: structlog>=25.5.0; extra == "example"
17
+ Requires-Dist: rich>=14.2.0; extra == "example"
18
+ Provides-Extra: tests
19
+ Requires-Dist: pytest>=8.4.2; extra == "tests"
20
+ Requires-Dist: pytest-asyncio>=1.2.0; extra == "tests"
21
+ Requires-Dist: httpx>=0.28.1; extra == "tests"
22
+ Requires-Dist: pytest-mock>=3.15.1; extra == "tests"
23
+ Requires-Dist: sqlalchemy>=2.0.41; extra == "tests"
24
+ Requires-Dist: aiosqlite>=0.22.1; extra == "tests"
25
+ Requires-Dist: factory-boy>=3.3.3; extra == "tests"
26
+ Requires-Dist: pyjwt>=2.10.1; extra == "tests"
27
+ Provides-Extra: scalar
28
+ Requires-Dist: scalar-fastapi>=1.5.0; extra == "scalar"
29
+ Dynamic: license-file
30
+
31
+ # Brilliance Admin Backend
32
+
33
+ Brilliance Admin Backend is a backend framework for building admin panels with Python and FastAPI.
34
+
35
+ - Serves a prebuilt SPA frontend as static files
36
+ - Generates schemas for frontend sections on the backend
37
+ - Provides a backend-driven API for admin interfaces
38
+ - Designed for fast data management and data viewing from any sources
39
+ - Inspired by Django Admin and Django REST Framework
40
+ - Focused on minimal boilerplate and simplified backend-controlled configuration
41
+
42
+ Features:
43
+ -
44
+ [Live Demo](https://brilliance-admin.com/)
45
+
46
+
47
+ ## Development
48
+
49
+ ``` shell
50
+ uv sync --all-groups --all-extras
51
+ uv run uvicorn example.main:app --host 0.0.0.0 --port 8082 --reload
52
+ ```
53
+
54
+ Docs:
55
+ - `http://0.0.0.0:8082/docs`
56
+ - `http://0.0.0.0:8082/redoc`
57
+ - `http://0.0.0.0:8082/scalar`
58
+
59
+ Tests:
60
+ ``` shell
61
+ uv run pytest
62
+ ```
63
+
64
+ ## Docker
65
+
66
+ ``` shell
67
+ docker compose -f .configs/docker/docker-compose.yml build
68
+ docker compose -f .configs/docker/docker-compose.yml up
69
+ docker compose -f .configs/docker/docker-compose.yml run --rm backend /bin/bash -c "uv sync --all-groups --all-extras"
70
+ docker compose -f .configs/docker/docker-compose.yml run --rm backend /bin/bash -c "uv run pytest"
71
+ ```
72
+
73
+ ``` shell
74
+ docker exec -it rollyum-backend-1 git config --global --add safe.directory '*'
75
+ docker exec -it rollyum-backend-1 uv run pre-commit run --all-files
76
+ ```
@@ -0,0 +1,46 @@
1
+ # Brilliance Admin Backend
2
+
3
+ Brilliance Admin Backend is a backend framework for building admin panels with Python and FastAPI.
4
+
5
+ - Serves a prebuilt SPA frontend as static files
6
+ - Generates schemas for frontend sections on the backend
7
+ - Provides a backend-driven API for admin interfaces
8
+ - Designed for fast data management and data viewing from any sources
9
+ - Inspired by Django Admin and Django REST Framework
10
+ - Focused on minimal boilerplate and simplified backend-controlled configuration
11
+
12
+ Features:
13
+ -
14
+ [Live Demo](https://brilliance-admin.com/)
15
+
16
+
17
+ ## Development
18
+
19
+ ``` shell
20
+ uv sync --all-groups --all-extras
21
+ uv run uvicorn example.main:app --host 0.0.0.0 --port 8082 --reload
22
+ ```
23
+
24
+ Docs:
25
+ - `http://0.0.0.0:8082/docs`
26
+ - `http://0.0.0.0:8082/redoc`
27
+ - `http://0.0.0.0:8082/scalar`
28
+
29
+ Tests:
30
+ ``` shell
31
+ uv run pytest
32
+ ```
33
+
34
+ ## Docker
35
+
36
+ ``` shell
37
+ docker compose -f .configs/docker/docker-compose.yml build
38
+ docker compose -f .configs/docker/docker-compose.yml up
39
+ docker compose -f .configs/docker/docker-compose.yml run --rm backend /bin/bash -c "uv sync --all-groups --all-extras"
40
+ docker compose -f .configs/docker/docker-compose.yml run --rm backend /bin/bash -c "uv run pytest"
41
+ ```
42
+
43
+ ``` shell
44
+ docker exec -it rollyum-backend-1 git config --global --add safe.directory '*'
45
+ docker exec -it rollyum-backend-1 uv run pre-commit run --all-files
46
+ ```
@@ -0,0 +1,4 @@
1
+ # pylint: disable=wildcard-import, unused-wildcard-import, unused-import
2
+ # flake8: noqa: F405
3
+ from admin_panel.integrations import sqlalchemy
4
+ from admin_panel import schema
File without changes
@@ -0,0 +1,18 @@
1
+ from fastapi import APIRouter
2
+
3
+ from .views.schema import router as schema_router
4
+ from .views.table import router as schema_table
5
+ from .views.auth import router as schema_auth
6
+ from .views.autocomplete import router as schema_autocomplete
7
+ from .views.graphs import router as schema_graphs
8
+ from .views.settings import router as schema_settings
9
+ from .views.index import router as schema_index
10
+
11
+ admin_panel_router = APIRouter()
12
+ admin_panel_router.include_router(schema_router)
13
+ admin_panel_router.include_router(schema_table)
14
+ admin_panel_router.include_router(schema_auth)
15
+ admin_panel_router.include_router(schema_autocomplete)
16
+ admin_panel_router.include_router(schema_graphs)
17
+ admin_panel_router.include_router(schema_settings)
18
+ admin_panel_router.include_router(schema_index)
@@ -0,0 +1,28 @@
1
+ from fastapi import HTTPException
2
+
3
+ from admin_panel.auth import AdminAuthentication
4
+
5
+
6
+ async def get_user(request):
7
+ auth: AdminAuthentication = request.app.state.schema.auth
8
+ user = await auth.authenticate(request.headers)
9
+ return user
10
+
11
+
12
+ async def get_category(request, group: str, category: str, check_type=None):
13
+ user = await get_user(request)
14
+
15
+ schema_group = request.app.state.schema.get_group(group)
16
+ if not schema_group:
17
+ raise HTTPException(status_code=404, detail="Group not found")
18
+
19
+ schema_category = schema_group.get_category(category)
20
+ if not schema_category:
21
+ raise HTTPException(status_code=404, detail="Category not found")
22
+
23
+ if check_type:
24
+ schema_category, user = await get_category(request, group, category)
25
+ if not issubclass(schema_category.__class__, check_type):
26
+ raise HTTPException(status_code=404, detail=f"Category {group}.{category} is not a {check_type.__name__}")
27
+
28
+ return schema_category, user
@@ -0,0 +1,29 @@
1
+ from fastapi import APIRouter, Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ from admin_panel.auth import AdminAuthentication, AuthData, AuthResult
5
+ from admin_panel.exceptions import AdminAPIException, APIError
6
+ from admin_panel.schema.admin_schema import AdminSchema
7
+ from admin_panel.translations import LanguageManager
8
+
9
+ router = APIRouter(prefix="/auth", tags=["Auth"])
10
+
11
+
12
+ @router.post(
13
+ path='/login/',
14
+ responses={401: {"model": APIError}},
15
+ )
16
+ async def login(request: Request, auth_data: AuthData) -> AuthResult:
17
+ schema: AdminSchema = request.app.state.schema
18
+
19
+ language_slug = request.headers.get('Accept-Language')
20
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
21
+ context = {'language_manager': language_manager}
22
+
23
+ auth: AdminAuthentication = schema.auth
24
+ try:
25
+ result: AuthResult = await auth.login(auth_data)
26
+ except AdminAPIException as e:
27
+ return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
28
+
29
+ return JSONResponse(content=result.model_dump(mode='json', context=context))
@@ -0,0 +1,33 @@
1
+ from fastapi import APIRouter, Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ from admin_panel.api.utils import get_category
5
+ from admin_panel.exceptions import AdminAPIException
6
+ from admin_panel.schema.admin_schema import AdminSchema
7
+ from admin_panel.schema.table.table_models import AutocompleteData, AutocompleteResult
8
+ from admin_panel.translations import LanguageManager
9
+ from admin_panel.utils import get_logger
10
+
11
+ router = APIRouter(prefix="/autocomplete", tags=["Autocomplete"])
12
+
13
+ logger = get_logger()
14
+
15
+
16
+ @router.post(path='/{group}/{category}/')
17
+ async def autocomplete(request: Request, group: str, category: str, data: AutocompleteData):
18
+ schema: AdminSchema = request.app.state.schema
19
+ schema_category, user = await get_category(request, group, category)
20
+
21
+ language_slug = request.headers.get('Accept-Language')
22
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
23
+ context = {'language_manager': language_manager}
24
+
25
+ try:
26
+ result: AutocompleteResult = await schema_category.autocomplete(data, user, language_manager)
27
+ except AdminAPIException as e:
28
+ return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
29
+ except Exception as e:
30
+ logger.exception('Autocomplete %s.%s exceptoin: %s', e, group, category, extra={'data': data})
31
+ return JSONResponse({}, status_code=500)
32
+
33
+ return JSONResponse(result.model_dump(mode='json', context=context))
@@ -0,0 +1,30 @@
1
+ from fastapi import APIRouter, Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ from admin_panel.api.utils import get_category
5
+ from admin_panel.exceptions import AdminAPIException
6
+ from admin_panel.schema.admin_schema import AdminSchema
7
+ from admin_panel.schema.graphs.category_graphs import CategoryGraphs, GraphData, GraphsDataResult
8
+ from admin_panel.translations import LanguageManager
9
+ from admin_panel.utils import get_logger
10
+
11
+ router = APIRouter(prefix="/graph", tags=["Category - Graph"])
12
+
13
+ logger = get_logger()
14
+
15
+
16
+ @router.post(path='/{group}/{category}/')
17
+ async def graph_data(request: Request, group: str, category: str, data: GraphData) -> GraphsDataResult:
18
+ schema: AdminSchema = request.app.state.schema
19
+ schema_category, user = await get_category(request, group, category, check_type=CategoryGraphs)
20
+
21
+ result: GraphsDataResult = await schema_category.get_data(data, user)
22
+
23
+ language_slug = request.headers.get('Accept-Language')
24
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
25
+ context = {'language_manager': language_manager}
26
+
27
+ try:
28
+ return JSONResponse(result.model_dump(mode='json', context=context))
29
+ except AdminAPIException as e:
30
+ return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
@@ -0,0 +1,38 @@
1
+ from fastapi import APIRouter, HTTPException, Request
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.templating import Jinja2Templates
4
+ from jinja2 import Environment, PackageLoader, select_autoescape
5
+
6
+ from admin_panel.schema import AdminSchema
7
+
8
+ router = APIRouter()
9
+
10
+ templates = Jinja2Templates(
11
+ env=Environment(
12
+ loader=PackageLoader("admin_panel", "templates"),
13
+ autoescape=select_autoescape(["html", "xml"]),
14
+ )
15
+ )
16
+
17
+ # Всё, что не должно попадать в SPA (можете расширять список)
18
+ EXACT_BLOCK = {"/openapi.json"}
19
+ PREFIX_BLOCK = ("/docs", "/redoc", "/scalar", "/static")
20
+
21
+
22
+ @router.get('/{rest_of_path:path}', response_class=HTMLResponse, include_in_schema=False)
23
+ async def admin_index(request: Request, rest_of_path: str):
24
+ '''
25
+ The request responds with a pre-rendered SPA served as an HTML page.
26
+ '''
27
+
28
+ path = "/" + rest_of_path
29
+ if path in EXACT_BLOCK or path.startswith(PREFIX_BLOCK):
30
+ raise HTTPException(status_code=404)
31
+
32
+ schema: AdminSchema = request.app.state.schema
33
+
34
+ return templates.TemplateResponse(
35
+ request=request,
36
+ name='index.html',
37
+ context=await schema.get_index_context_data(request),
38
+ )
@@ -0,0 +1,29 @@
1
+ from fastapi import APIRouter, Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ from admin_panel.auth import AdminAuthentication
5
+ from admin_panel.exceptions import AdminAPIException, APIError
6
+ from admin_panel.schema import AdminSchema, AdminSchemaData
7
+
8
+ router = APIRouter(prefix="/schema", tags=["Main admin schema"])
9
+
10
+
11
+ @router.get(
12
+ path='/',
13
+ responses={400: {"model": APIError}},
14
+ )
15
+ async def schema_handler(request: Request) -> AdminSchemaData:
16
+ '''
17
+ Request for retrieving the admin panel schema, including all sections and their contents.
18
+ '''
19
+ schema: AdminSchema = request.app.state.schema
20
+
21
+ auth: AdminAuthentication = schema.auth
22
+ try:
23
+ user = await auth.authenticate(request.headers)
24
+ except AdminAPIException as e:
25
+ return JSONResponse(e.get_error().model_dump(mode='json'), status_code=e.status_code)
26
+
27
+ language_slug = request.headers.get('Accept-Language')
28
+ admin_schema = schema.generate_schema(user, language_slug)
29
+ return admin_schema