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.
- brilliance_admin-0.39.0/.configs/docker/Dockerfile +22 -0
- brilliance_admin-0.39.0/.configs/docker/docker-compose.yml +35 -0
- brilliance_admin-0.39.0/.configs/nginx/example.conf +25 -0
- brilliance_admin-0.39.0/.env +0 -0
- brilliance_admin-0.39.0/.github/workflows/certbot.yml +27 -0
- brilliance_admin-0.39.0/.github/workflows/deploy.yml +127 -0
- brilliance_admin-0.39.0/.github/workflows/install-docker.yml +26 -0
- brilliance_admin-0.39.0/.gitignore +11 -0
- brilliance_admin-0.39.0/.isort.cfg +7 -0
- brilliance_admin-0.39.0/.python-version +1 -0
- brilliance_admin-0.39.0/LICENSE +17 -0
- brilliance_admin-0.39.0/PKG-INFO +76 -0
- brilliance_admin-0.39.0/README.md +46 -0
- brilliance_admin-0.39.0/admin_panel/__init__.py +4 -0
- brilliance_admin-0.39.0/admin_panel/api/__init__.py +0 -0
- brilliance_admin-0.39.0/admin_panel/api/routers.py +18 -0
- brilliance_admin-0.39.0/admin_panel/api/utils.py +28 -0
- brilliance_admin-0.39.0/admin_panel/api/views/__init__.py +0 -0
- brilliance_admin-0.39.0/admin_panel/api/views/auth.py +29 -0
- brilliance_admin-0.39.0/admin_panel/api/views/autocomplete.py +33 -0
- brilliance_admin-0.39.0/admin_panel/api/views/graphs.py +30 -0
- brilliance_admin-0.39.0/admin_panel/api/views/index.py +38 -0
- brilliance_admin-0.39.0/admin_panel/api/views/schema.py +29 -0
- brilliance_admin-0.39.0/admin_panel/api/views/settings.py +29 -0
- brilliance_admin-0.39.0/admin_panel/api/views/table.py +136 -0
- brilliance_admin-0.39.0/admin_panel/auth.py +32 -0
- brilliance_admin-0.39.0/admin_panel/docs.py +37 -0
- brilliance_admin-0.39.0/admin_panel/exceptions.py +38 -0
- brilliance_admin-0.39.0/admin_panel/integrations/__init__.py +0 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/__init__.py +6 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/auth.py +144 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/autocomplete.py +38 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/fields.py +254 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/fields_schema.py +316 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/__init__.py +19 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/base.py +141 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/create.py +73 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/delete.py +18 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/list.py +178 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/retrieve.py +61 -0
- brilliance_admin-0.39.0/admin_panel/integrations/sqlalchemy/table/update.py +95 -0
- brilliance_admin-0.39.0/admin_panel/schema/__init__.py +7 -0
- brilliance_admin-0.39.0/admin_panel/schema/admin_schema.py +185 -0
- brilliance_admin-0.39.0/admin_panel/schema/category.py +149 -0
- brilliance_admin-0.39.0/admin_panel/schema/graphs/__init__.py +1 -0
- brilliance_admin-0.39.0/admin_panel/schema/graphs/category_graphs.py +50 -0
- brilliance_admin-0.39.0/admin_panel/schema/group.py +67 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/__init__.py +8 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/admin_action.py +76 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/category_table.py +175 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/fields/__init__.py +5 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/fields/base.py +249 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/fields/function_field.py +65 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/fields_schema.py +216 -0
- brilliance_admin-0.39.0/admin_panel/schema/table/table_models.py +53 -0
- brilliance_admin-0.39.0/admin_panel/static/favicon.ico +0 -0
- brilliance_admin-0.39.0/admin_panel/static/index-BrXRRuaE.css +8 -0
- brilliance_admin-0.39.0/admin_panel/static/index-SCeDXvci.js +525 -0
- brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
- brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
- brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
- brilliance_admin-0.39.0/admin_panel/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-first/content.min.css +250 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-first/skin.min.css +2820 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-slim/content.min.css +249 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/dark-slim/skin.min.css +2821 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/img/example.png +0 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/img/tinymce.woff2 +0 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/lightgray/content.min.css +1 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/lightgray/skin.min.css +1 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/accordion/css/accordion.css +17 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/accordion/plugin.js +48 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/codesample/css/prism.css +1 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/customLink/css/link.css +3 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/plugins/customLink/plugin.js +147 -0
- brilliance_admin-0.39.0/admin_panel/static/tinymce/tinymce.min.js +2 -0
- brilliance_admin-0.39.0/admin_panel/static/vanilla-picker-B6E6ObS_.js +8 -0
- brilliance_admin-0.39.0/admin_panel/templates/index.html +25 -0
- brilliance_admin-0.39.0/admin_panel/translations.py +145 -0
- brilliance_admin-0.39.0/admin_panel/utils.py +50 -0
- brilliance_admin-0.39.0/brilliance_admin.egg-info/PKG-INFO +76 -0
- brilliance_admin-0.39.0/brilliance_admin.egg-info/SOURCES.txt +109 -0
- brilliance_admin-0.39.0/brilliance_admin.egg-info/dependency_links.txt +1 -0
- brilliance_admin-0.39.0/brilliance_admin.egg-info/requires.txt +23 -0
- brilliance_admin-0.39.0/brilliance_admin.egg-info/top_level.txt +1 -0
- brilliance_admin-0.39.0/example/__init__.py +0 -0
- brilliance_admin-0.39.0/example/main.py +142 -0
- brilliance_admin-0.39.0/example/phrases.py +112 -0
- brilliance_admin-0.39.0/example/sections/__init__.py +0 -0
- brilliance_admin-0.39.0/example/sections/currency.py +20 -0
- brilliance_admin-0.39.0/example/sections/graphs.py +136 -0
- brilliance_admin-0.39.0/example/sections/merchant.py +45 -0
- brilliance_admin-0.39.0/example/sections/models.py +194 -0
- brilliance_admin-0.39.0/example/sections/payments.py +199 -0
- brilliance_admin-0.39.0/example/sections/terminal.py +41 -0
- brilliance_admin-0.39.0/example/sections/users.py +25 -0
- brilliance_admin-0.39.0/example/sqlite.py +45 -0
- brilliance_admin-0.39.0/example/utils.py +26 -0
- brilliance_admin-0.39.0/pyproject.toml +47 -0
- brilliance_admin-0.39.0/setup.cfg +4 -0
- brilliance_admin-0.39.0/tests/__init__.py +0 -0
- brilliance_admin-0.39.0/tests/conftest.py +12 -0
- brilliance_admin-0.39.0/tests/test_action.py +26 -0
- brilliance_admin-0.39.0/tests/test_payments_fields_schema.py +222 -0
- brilliance_admin-0.39.0/tests/test_sqlalcmeny_auth.py +98 -0
- brilliance_admin-0.39.0/tests/test_sqlalcmeny_crud.py +275 -0
- brilliance_admin-0.39.0/tests/test_sqlalcmeny_filters.py +229 -0
- brilliance_admin-0.39.0/tests/test_sqlalcmeny_schema.py +159 -0
- brilliance_admin-0.39.0/tests/test_translations.py +90 -0
- 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 @@
|
|
|
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
|
+
```
|
|
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
|
|
File without changes
|
|
@@ -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
|