shaapi 0.1.0__py3-none-any.whl
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.
- shaapi/__init__.py +3 -0
- shaapi/cli.py +97 -0
- shaapi/generator.py +114 -0
- shaapi/template/.dockerignore +37 -0
- shaapi/template/.env.template +59 -0
- shaapi/template/.gitattributes +12 -0
- shaapi/template/.gitignore +170 -0
- shaapi/template/.gitlab-ci.yml +89 -0
- shaapi/template/Dockerfile +59 -0
- shaapi/template/LICENSE +21 -0
- shaapi/template/README.md +206 -0
- shaapi/template/backend/.gitignore +164 -0
- shaapi/template/backend/__init__.py +0 -0
- shaapi/template/backend/alembic/README +1 -0
- shaapi/template/backend/alembic/env.py +102 -0
- shaapi/template/backend/alembic/script.py.mako +26 -0
- shaapi/template/backend/alembic/versions/2026_06_08_1024-64524c63b666_initial.py +143 -0
- shaapi/template/backend/alembic.ini +117 -0
- shaapi/template/backend/app/__init__.py +55 -0
- shaapi/template/backend/app/admin/__init__.py +1 -0
- shaapi/template/backend/app/admin/api/v1/__init__.py +0 -0
- shaapi/template/backend/app/admin/api/v1/auth.py +59 -0
- shaapi/template/backend/app/admin/api/v1/casbin.py +218 -0
- shaapi/template/backend/app/admin/api/v1/login_log.py +63 -0
- shaapi/template/backend/app/admin/api/v1/opera_log.py +61 -0
- shaapi/template/backend/app/admin/api/v1/role.py +108 -0
- shaapi/template/backend/app/admin/api/v1/user.py +47 -0
- shaapi/template/backend/app/admin/schema/casbin_rule.py +45 -0
- shaapi/template/backend/app/admin/schema/login_log.py +36 -0
- shaapi/template/backend/app/admin/schema/opera_log.py +43 -0
- shaapi/template/backend/app/admin/schema/role.py +36 -0
- shaapi/template/backend/app/admin/schema/sso.py +37 -0
- shaapi/template/backend/app/admin/schema/token.py +74 -0
- shaapi/template/backend/app/admin/schema/user.py +93 -0
- shaapi/template/backend/app/admin/service/auth_service.py +233 -0
- shaapi/template/backend/app/admin/service/casbin_service.py +135 -0
- shaapi/template/backend/app/admin/service/login_log_service.py +62 -0
- shaapi/template/backend/app/admin/service/opera_log_service.py +31 -0
- shaapi/template/backend/app/admin/service/role_service.py +79 -0
- shaapi/template/backend/app/admin/service/secure_token_service.py +60 -0
- shaapi/template/backend/app/admin/service/user_service.py +153 -0
- shaapi/template/backend/app/api.py +11 -0
- shaapi/template/backend/common/__init__.py +0 -0
- shaapi/template/backend/common/cloud_storage/__init__.py +11 -0
- shaapi/template/backend/common/cloud_storage/cloud_storage.py +180 -0
- shaapi/template/backend/common/dataclasses.py +52 -0
- shaapi/template/backend/common/email_conf/email.py +105 -0
- shaapi/template/backend/common/enums.py +144 -0
- shaapi/template/backend/common/exception/__init__.py +0 -0
- shaapi/template/backend/common/exception/errors.py +87 -0
- shaapi/template/backend/common/exception/exception_handler.py +280 -0
- shaapi/template/backend/common/log.py +123 -0
- shaapi/template/backend/common/model.py +68 -0
- shaapi/template/backend/common/pagination.py +83 -0
- shaapi/template/backend/common/response/__init__.py +0 -0
- shaapi/template/backend/common/response/response_code.py +158 -0
- shaapi/template/backend/common/response/response_schema.py +110 -0
- shaapi/template/backend/common/schema.py +144 -0
- shaapi/template/backend/common/security/jwt.py +203 -0
- shaapi/template/backend/common/security/rbac.py +98 -0
- shaapi/template/backend/common/security/sec_token.py +6 -0
- shaapi/template/backend/common/socketio/action.py +11 -0
- shaapi/template/backend/common/socketio/server.py +50 -0
- shaapi/template/backend/common/sso/base.py +69 -0
- shaapi/template/backend/common/sso/google.py +127 -0
- shaapi/template/backend/core/conf.py +208 -0
- shaapi/template/backend/core/path_conf.py +24 -0
- shaapi/template/backend/core/registrar.py +195 -0
- shaapi/template/backend/crud/__init__.py +1 -0
- shaapi/template/backend/crud/crud_base.py +35 -0
- shaapi/template/backend/crud/crud_casbin.py +46 -0
- shaapi/template/backend/crud/crud_login_log.py +58 -0
- shaapi/template/backend/crud/crud_opera_log.py +58 -0
- shaapi/template/backend/crud/crud_role.py +128 -0
- shaapi/template/backend/crud/crud_user.py +267 -0
- shaapi/template/backend/database/__init__.py +0 -0
- shaapi/template/backend/database/db_postgres.py +125 -0
- shaapi/template/backend/database/db_redis.py +62 -0
- shaapi/template/backend/entrypoint-api.sh +19 -0
- shaapi/template/backend/lang/en/app.py +18 -0
- shaapi/template/backend/lang/en/auth.py +10 -0
- shaapi/template/backend/lang/fr/app.py +18 -0
- shaapi/template/backend/lang/fr/auth.py +10 -0
- shaapi/template/backend/main.py +54 -0
- shaapi/template/backend/middleware/__init__.py +1 -0
- shaapi/template/backend/middleware/access_middleware.py +19 -0
- shaapi/template/backend/middleware/i18n_middleware.py +19 -0
- shaapi/template/backend/middleware/jwt_auth_middleware.py +73 -0
- shaapi/template/backend/middleware/opera_log_middleware.py +179 -0
- shaapi/template/backend/middleware/state_middleware.py +26 -0
- shaapi/template/backend/models/__init__.py +10 -0
- shaapi/template/backend/models/associations.py +20 -0
- shaapi/template/backend/models/casbin_rule.py +30 -0
- shaapi/template/backend/models/login_log.py +28 -0
- shaapi/template/backend/models/opera_log.py +36 -0
- shaapi/template/backend/models/role.py +27 -0
- shaapi/template/backend/models/user.py +30 -0
- shaapi/template/backend/seeder/json/admin.json +15 -0
- shaapi/template/backend/seeder/json/user.json +15 -0
- shaapi/template/backend/seeder/run.py +34 -0
- shaapi/template/backend/static/ip2region.xdb +0 -0
- shaapi/template/backend/templates/build/meet.html +169 -0
- shaapi/template/backend/templates/build/new_account.html +373 -0
- shaapi/template/backend/templates/build/reset-password.html +170 -0
- shaapi/template/backend/templates/build/test_email.html +25 -0
- shaapi/template/backend/templates/build/welcome-one-1.html +160 -0
- shaapi/template/backend/templates/build/welcome-one.html +178 -0
- shaapi/template/backend/templates/build/welcome-two.html +234 -0
- shaapi/template/backend/templates/index.html +0 -0
- shaapi/template/backend/templates/src/new_account.mjml +15 -0
- shaapi/template/backend/templates/src/reset_password.mjml +19 -0
- shaapi/template/backend/templates/src/test_email.mjml +11 -0
- shaapi/template/backend/templates/ws/ws.html +70 -0
- shaapi/template/backend/utils/demo_site.py +18 -0
- shaapi/template/backend/utils/encrypt.py +108 -0
- shaapi/template/backend/utils/health_check.py +34 -0
- shaapi/template/backend/utils/prometheus.py +135 -0
- shaapi/template/backend/utils/request_parse.py +110 -0
- shaapi/template/backend/utils/serializers.py +75 -0
- shaapi/template/backend/utils/timezone.py +51 -0
- shaapi/template/backend/utils/trace_id.py +7 -0
- shaapi/template/backend/utils/translator.py +28 -0
- shaapi/template/devops/scripts/deploy.sh +7 -0
- shaapi/template/devops/scripts/setup_env.sh +62 -0
- shaapi/template/docker-compose.monitoring.yml +63 -0
- shaapi/template/docker-compose.override.yml +12 -0
- shaapi/template/docker-compose.yml +90 -0
- shaapi/template/docker-run.sh +99 -0
- shaapi/template/etc/dashboards/fastapi-observability.json +1044 -0
- shaapi/template/etc/dashboards.yaml +10 -0
- shaapi/template/etc/grafana/datasource.yml +79 -0
- shaapi/template/etc/prometheus/prometheus.yml +52 -0
- shaapi/template/package-lock.json +2102 -0
- shaapi/template/package.json +16 -0
- shaapi/template/pyproject.toml +78 -0
- shaapi/template/uv.lock +2866 -0
- shaapi-0.1.0.dist-info/METADATA +92 -0
- shaapi-0.1.0.dist-info/RECORD +141 -0
- shaapi-0.1.0.dist-info/WHEEL +4 -0
- shaapi-0.1.0.dist-info/entry_points.txt +2 -0
- shaapi-0.1.0.dist-info/licenses/LICENCE +21 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# FastAPI REST API boilerplate
|
|
2
|
+
|
|
3
|
+
## Description <!-- omit in toc -->
|
|
4
|
+
|
|
5
|
+
FastAPI REST API boilerplate is a comprehensive starting point for developing robust web applications. It provides a structured foundation equipped with essential features and configurations.
|
|
6
|
+
|
|
7
|
+
<img src=".github/preview.png" alt="Swagger preview" />
|
|
8
|
+
|
|
9
|
+
## Table of Contents <!-- omit in toc -->
|
|
10
|
+
|
|
11
|
+
- [Features](#features)
|
|
12
|
+
- [Project structure](#project-structure)
|
|
13
|
+
- [Environmnent variables](#environmnent-variables)
|
|
14
|
+
- [Quick run](#quick-run)
|
|
15
|
+
- [Comfortable development](#comfortable-development)
|
|
16
|
+
- [Links](#links)
|
|
17
|
+
- [x] Config Service ([Pydantic](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)).
|
|
18
|
+
- [Database utils](#database-utils)
|
|
19
|
+
- [Tests in Docker](#tests-in-docker)
|
|
20
|
+
<!-- - [Tests](#tests)
|
|
21
|
+
- [Test benchmarking](#test-benchmarking) -->
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- [x] Database ([Sqlalchemy](https://www.sqlalchemy.org)).
|
|
26
|
+
- [x] Database migration ([Alembic](https://alembic.sqlalchemy.org))
|
|
27
|
+
- [x] Swagger.
|
|
28
|
+
- [x] Redoc.
|
|
29
|
+
- [x] Sign in and sign up via email.
|
|
30
|
+
- [x] Social sign in (apple, facebook, google, linkedin, microsoft)
|
|
31
|
+
- [x] Seeding ([sqlalchemyseed](https://sqlalchemyseed.readthedocs.io/en/stable/)).
|
|
32
|
+
- [x] Login logs, operation logs, app logs.
|
|
33
|
+
- [x] Mailing
|
|
34
|
+
- [x] File uploads (aws s3, cloudinary, google cloud storage, minio)
|
|
35
|
+
- [x] Redis
|
|
36
|
+
- [x] Docker.
|
|
37
|
+
- [x] Admin and User roles using RBAC ([Casbin](https://casbin.org/fr/docs/rbac)).
|
|
38
|
+
- [x] Monitoring using grafana and prometheus ([Grafana](https://grafana.com/))
|
|
39
|
+
- [x] I18N
|
|
40
|
+
- [x] Versionning ([https://github.com/conventional-changelog/standard-version])
|
|
41
|
+
- [ ] Elasticseach (using [pgsync](https://pgsync.com/), [elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html))
|
|
42
|
+
- [ ] Admin dashboard using ([sqladmin](https://aminalaee.dev/sqladmin/))
|
|
43
|
+
- [ ] E2E and units tests.
|
|
44
|
+
- [ ] Realtime notification using [messaging queue](https://www.rabbitmq.com/) and [python-socketio](https://python-socketio.readthedocs.io/en/latest/server.html)
|
|
45
|
+
- [ ] CI ([Gitlab](https://docs.gitlab.com/ee/ci/)).
|
|
46
|
+
- [ ] Precommit ([Pre-commit](https://pre-commit.com/))
|
|
47
|
+
|
|
48
|
+
## Project Structure
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
seeder # Configurtion for database seeding
|
|
52
|
+
migrations # Alembic migration files
|
|
53
|
+
app # Rest api files
|
|
54
|
+
app.core # General components like config, security, types, role, etc...
|
|
55
|
+
app.db # Database connection specific
|
|
56
|
+
app.crud # CRUD for types from models
|
|
57
|
+
app.models # Sqlalchemy models
|
|
58
|
+
app.schemas # Pydantic models that used in crud or handlers
|
|
59
|
+
app.templates # Html files for mails
|
|
60
|
+
app.endpoints # Restapi endpoints files
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
├── backend
|
|
65
|
+
| |
|
|
66
|
+
│ ├── alembic
|
|
67
|
+
│ ├── core
|
|
68
|
+
│ ├── common
|
|
69
|
+
│ ├── app
|
|
70
|
+
│ │ ├── admin
|
|
71
|
+
│ │ |
|
|
72
|
+
│ │ └── api.py
|
|
73
|
+
│ ├── crud
|
|
74
|
+
│ ├── database
|
|
75
|
+
│ ├── lang
|
|
76
|
+
| ├── middleware
|
|
77
|
+
│ └── models
|
|
78
|
+
│ └── schemas
|
|
79
|
+
│ └── seeder
|
|
80
|
+
│ └── static
|
|
81
|
+
│ └── templates
|
|
82
|
+
│ └── utils
|
|
83
|
+
|
|
|
84
|
+
├── devops
|
|
85
|
+
├── etc
|
|
86
|
+
|── .vscode
|
|
87
|
+
|── .github
|
|
88
|
+
|──...
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Environmnent variables
|
|
93
|
+
|
|
94
|
+
To correctly run the project, you will need some environment variables. Expose & import them in core/config.py
|
|
95
|
+
|
|
96
|
+
- `ENVIRONMENT`: Specifies the current runtime environment of the application. Possible values include dev, prod, and preprod
|
|
97
|
+
- `POSTGRES_HOST`: Hostname or address of the PostgreSQL database server.
|
|
98
|
+
- `POSTGRES_PORT`: Port on which the PostgreSQL database server is listening (default is 5432).
|
|
99
|
+
- `POSTGRES_USER`: Username used to authenticate with the PostgreSQL database.
|
|
100
|
+
- `POSTGRES_PASSWORD`: Password for the PostgreSQL user.
|
|
101
|
+
- `REDIS_HOST`: Hostname or address of the Redis server.
|
|
102
|
+
- `REDIS_POR`: Port on which the Redis server is running (default is 6379).
|
|
103
|
+
- `REDIS_PASSWOR`: Password for the Redis server. If blank, no authentication is required.
|
|
104
|
+
- `REDIS_DATABAS`: Database index in Redis to use (default is 0).
|
|
105
|
+
- `OTLP_GRPC_ENDPOIN`: gRPC endpoint for sending OpenTelemetry logs and traces, typically to Tempo or similar systems.
|
|
106
|
+
- `TOKEN_SECRET_KE`: Secret key used for generating and verifying JWT tokens.
|
|
107
|
+
- `OPERA_LOG_ENCRYPT_SECRET_KE`: Encryption key used to securely encrypt log data for operations.
|
|
108
|
+
- `OAUTH2_GITHUB_CLIENT_I`: Client ID for GitHub OAuth2 integration.
|
|
109
|
+
- `OAUTH2_GITHUB_CLIENT_SECRE`: Client secret for GitHub OAuth2 integration.
|
|
110
|
+
- `OAUTH2_LINUX_DO_CLIENT_I`: Client ID for Linux DO OAuth2 integration.
|
|
111
|
+
- `OAUTH2_LINUX_DO_CLIENT_SECRE`: Client secret for Linux DO OAuth2 integration.
|
|
112
|
+
- `CELERY_BROKER_REDIS_DATABAS`: Redis database index for the Celery task broker.
|
|
113
|
+
- `CELERY_BACKEND_REDIS_DATABAS`: Redis database index for Celery task results backend.
|
|
114
|
+
- `RABBITMQ_HOS`: Hostname or address of the RabbitMQ message broker.
|
|
115
|
+
- `RABBITMQ_POR`: Port on which RabbitMQ is running (default is 5672).
|
|
116
|
+
- `RABBITMQ_USERNAM`: Username for authenticating with RabbitMQ.
|
|
117
|
+
- `RABBITMQ_PASSWOR`: Password for the RabbitMQ user.
|
|
118
|
+
- `MINIO_ENDPOIN`: Hostname or address for the MinIO server.
|
|
119
|
+
- `MINIO_POR`: Port on which MinIO is running (default is 9000).
|
|
120
|
+
- `MINIO_ACCESS_KE`: Access key for authenticating with the MinIO server.
|
|
121
|
+
- `MINIO_SECRET_KE`: Secret key for authenticating with the MinIO server.
|
|
122
|
+
- `MINIO_BUCKET_NAM`: Name of the MinIO bucket used for storing files.
|
|
123
|
+
- `MINIO_CLOUD_UR`: Cloud URL for accessing MinIO content externally.
|
|
124
|
+
- `SMTP_TL`: Boolean value indicating whether TLS is enabled for SMTP communication (True for enabled).
|
|
125
|
+
- `SMTP_POR`: Port on which the SMTP server listens for connections (default is 587 for TLS).
|
|
126
|
+
- `SMTP_HOS`: Hostname or address of the SMTP email server.
|
|
127
|
+
- `SMTP_USE`: Username for authenticating with the SMTP server (typically an email address).
|
|
128
|
+
- `EMAILS_FROM_EMAI`: Email address from which emails will be sent.
|
|
129
|
+
- `EMAILS_FROM_NAM`: Display name associated with the sender's email address.
|
|
130
|
+
- `SMTP_PASSWOR`: Password for the SMTP user account.
|
|
131
|
+
- `GOOGLE_CLIENT_I`: Client ID for Google OAuth2 integration.
|
|
132
|
+
- `GOOGLE_SECRET_KE`: Secret key for Google OAuth2 integration.
|
|
133
|
+
- `GOOGLE_WEBHOOK_OAUTH_REDIRECT_UR`: Redirect URI used during Google OAuth2 authentication flow.
|
|
134
|
+
- `GENAI_API_KE`: API key for accessing Generative AI services.
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## Quick run
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone --depth 1 https://github.com/kaanari-tech/fastapi-boilerplate.git my-app
|
|
141
|
+
cd my-app/devops/docker-compose
|
|
142
|
+
docker plugin install grafana/loki-docker-driver:2.9.2 --alias loki --grant-all-permissions
|
|
143
|
+
docker-compose up -d --build
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
For check status run
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
docker-compose logs
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Comfortable development
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
git clone --depth 1 https://github.com/kaanari-tech/fastapi-boilerplate.git my-app
|
|
156
|
+
cd my-app/
|
|
157
|
+
cp .env.example .env
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Change `POSTGRES_HOST=boilerplate_postgres` to `POSTGRES_HOST=localhost`,
|
|
161
|
+
`POSTGRES_PORT=5432` to `POSTGRES_PORT=6001`
|
|
162
|
+
`RABBITMQ_HOST=boilerplate_rabbitmq` to `RABBITMQ_HOST=localhost`
|
|
163
|
+
`REDIS_HOST=boilerplate_redis` to `REDIS_HOST=localhost`
|
|
164
|
+
make sure you have [poetry](https://python-poetry.org) install
|
|
165
|
+
Run additional container:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
docker-compose up -d boilerplate_postgres boilerplateminio boilerplate_redis
|
|
169
|
+
poetry install
|
|
170
|
+
poe migrate
|
|
171
|
+
poe run
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Links
|
|
175
|
+
|
|
176
|
+
- Swagger: <http://localhost:8000/admin/api/v1/docs>
|
|
177
|
+
- Redoc: <http://localhost:8000/redoc>
|
|
178
|
+
- Grafana dashboard: <http://localhost:3000>
|
|
179
|
+
|
|
180
|
+
on Grafana `http://localhost:3000` login with admin:admin
|
|
181
|
+
|
|
182
|
+
## Database utils
|
|
183
|
+
|
|
184
|
+
Generate migration
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
poe makemigrations
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Run migration
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
poe migrate
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Revert migration
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
poe dwngrade
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Drop all tables in database
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
poe drop-tables
|
|
206
|
+
```
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
log/
|
|
2
|
+
# Byte-compiled / optimized / DLL files
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*$py.class
|
|
6
|
+
|
|
7
|
+
# C extensions
|
|
8
|
+
*.so
|
|
9
|
+
.env.elastic
|
|
10
|
+
|
|
11
|
+
# Distribution / packaging
|
|
12
|
+
.Python
|
|
13
|
+
# build/
|
|
14
|
+
develop-eggs/
|
|
15
|
+
dist/
|
|
16
|
+
downloads/
|
|
17
|
+
eggs/
|
|
18
|
+
.eggs/
|
|
19
|
+
lib/
|
|
20
|
+
lib64/
|
|
21
|
+
parts/
|
|
22
|
+
sdist/
|
|
23
|
+
var/
|
|
24
|
+
wheels/
|
|
25
|
+
share/python-wheels/
|
|
26
|
+
*.egg-info/
|
|
27
|
+
.installed.cfg
|
|
28
|
+
*.egg
|
|
29
|
+
MANIFEST
|
|
30
|
+
|
|
31
|
+
# private_key.pem
|
|
32
|
+
# public_key.pem
|
|
33
|
+
|
|
34
|
+
# PyInstaller
|
|
35
|
+
# Usually these files are written by a python script from a template
|
|
36
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
37
|
+
*.manifest
|
|
38
|
+
*.spec
|
|
39
|
+
|
|
40
|
+
# Installer logs
|
|
41
|
+
pip-log.txt
|
|
42
|
+
pip-delete-this-directory.txt
|
|
43
|
+
|
|
44
|
+
# Unit test / coverage reports
|
|
45
|
+
htmlcov/
|
|
46
|
+
.tox/
|
|
47
|
+
.nox/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
.cache
|
|
51
|
+
nosetests.xml
|
|
52
|
+
coverage.xml
|
|
53
|
+
*.cover
|
|
54
|
+
*.py,cover
|
|
55
|
+
.hypothesis/
|
|
56
|
+
.pytest_cache/
|
|
57
|
+
cover/
|
|
58
|
+
# /faiss_index
|
|
59
|
+
|
|
60
|
+
# Translations
|
|
61
|
+
*.mo
|
|
62
|
+
*.pot
|
|
63
|
+
|
|
64
|
+
# Django stuff:
|
|
65
|
+
*.log
|
|
66
|
+
local_settings.py
|
|
67
|
+
db.sqlite3
|
|
68
|
+
db.sqlite3-journal
|
|
69
|
+
|
|
70
|
+
# Flask stuff:
|
|
71
|
+
instance/
|
|
72
|
+
.webassets-cache
|
|
73
|
+
|
|
74
|
+
# Scrapy stuff:
|
|
75
|
+
.scrapy
|
|
76
|
+
|
|
77
|
+
# Sphinx documentation
|
|
78
|
+
docs/_build/
|
|
79
|
+
|
|
80
|
+
# PyBuilder
|
|
81
|
+
.pybuilder/
|
|
82
|
+
target/
|
|
83
|
+
|
|
84
|
+
# Jupyter Notebook
|
|
85
|
+
.ipynb_checkpoints
|
|
86
|
+
|
|
87
|
+
# IPython
|
|
88
|
+
profile_default/
|
|
89
|
+
ipython_config.py
|
|
90
|
+
|
|
91
|
+
# pyenv
|
|
92
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
93
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
94
|
+
# .python-version
|
|
95
|
+
|
|
96
|
+
# pipenv
|
|
97
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
98
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
99
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
100
|
+
# install all needed dependencies.
|
|
101
|
+
#Pipfile.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
111
|
+
__pypackages__/
|
|
112
|
+
|
|
113
|
+
# Celery stuff
|
|
114
|
+
celerybeat-schedule
|
|
115
|
+
celerybeat.pid
|
|
116
|
+
|
|
117
|
+
# SageMath parsed files
|
|
118
|
+
*.sage.py
|
|
119
|
+
|
|
120
|
+
# Environments
|
|
121
|
+
.env
|
|
122
|
+
.env.elastic
|
|
123
|
+
.venv
|
|
124
|
+
env/
|
|
125
|
+
venv/
|
|
126
|
+
ENV/
|
|
127
|
+
env.bak/
|
|
128
|
+
venv.bak/
|
|
129
|
+
|
|
130
|
+
# Spyder project settings
|
|
131
|
+
.spyderproject
|
|
132
|
+
.spyproject
|
|
133
|
+
|
|
134
|
+
# Rope project settings
|
|
135
|
+
.ropeproject
|
|
136
|
+
|
|
137
|
+
# mkdocs documentation
|
|
138
|
+
/site
|
|
139
|
+
|
|
140
|
+
# mypy
|
|
141
|
+
.mypy_cache/
|
|
142
|
+
.dmypy.json
|
|
143
|
+
dmypy.json
|
|
144
|
+
|
|
145
|
+
# Pyre type checker
|
|
146
|
+
.pyre/
|
|
147
|
+
|
|
148
|
+
# pytype static type analyzer
|
|
149
|
+
.pytype/
|
|
150
|
+
|
|
151
|
+
# Cython debug symbols
|
|
152
|
+
cython_debug/
|
|
153
|
+
|
|
154
|
+
# PyCharm
|
|
155
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
156
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
157
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
158
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
159
|
+
#.idea/
|
|
160
|
+
.history/
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
changelog.config.js
|
|
164
|
+
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Generic single-database configuration.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ruff: noqa: E402, F403, I001, RUF100
|
|
4
|
+
import asyncio
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from logging.config import fileConfig
|
|
9
|
+
|
|
10
|
+
from alembic import context
|
|
11
|
+
from sqlalchemy import engine_from_config, pool
|
|
12
|
+
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
13
|
+
|
|
14
|
+
sys.path.append('../')
|
|
15
|
+
|
|
16
|
+
from backend.core import path_conf
|
|
17
|
+
|
|
18
|
+
if not os.path.exists(path_conf.ALEMBIC_Versions_DIR):
|
|
19
|
+
os.makedirs(path_conf.ALEMBIC_Versions_DIR)
|
|
20
|
+
|
|
21
|
+
# this is the Alembic Config object, which provides
|
|
22
|
+
# access to the values within the .ini file in use.
|
|
23
|
+
config = context.config
|
|
24
|
+
|
|
25
|
+
# Interpret the config file for Python logging.
|
|
26
|
+
# This line sets up loggers basically.
|
|
27
|
+
fileConfig(config.config_file_name)
|
|
28
|
+
|
|
29
|
+
# add your model's MetaData object here
|
|
30
|
+
# for 'autogenerate' support
|
|
31
|
+
from backend.common.model import MappedBase
|
|
32
|
+
|
|
33
|
+
# if add new app, do like this
|
|
34
|
+
from backend.models import * # noqa: F401
|
|
35
|
+
from backend.models import * # noqa: F401
|
|
36
|
+
|
|
37
|
+
target_metadata = MappedBase.metadata
|
|
38
|
+
|
|
39
|
+
# other values from the config, defined by the needs of env.py,
|
|
40
|
+
from backend.database.db_postgres import SQLALCHEMY_DATABASE_URL
|
|
41
|
+
|
|
42
|
+
config.set_main_option('sqlalchemy.url', SQLALCHEMY_DATABASE_URL)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def run_migrations_offline():
|
|
46
|
+
"""Run migrations in 'offline' mode.
|
|
47
|
+
|
|
48
|
+
This configures the context with just a URL
|
|
49
|
+
and not an Engine, though an Engine is acceptable
|
|
50
|
+
here as well. By skipping the Engine creation
|
|
51
|
+
we don't even need a DBAPI to be available.
|
|
52
|
+
|
|
53
|
+
Calls to context.execute() here emit the given string to the
|
|
54
|
+
script output.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
url = config.get_main_option('sqlalchemy.url')
|
|
58
|
+
context.configure(
|
|
59
|
+
url=url,
|
|
60
|
+
target_metadata=target_metadata,
|
|
61
|
+
literal_binds=True,
|
|
62
|
+
dialect_opts={'paramstyle': 'named'},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
with context.begin_transaction():
|
|
66
|
+
context.run_migrations()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def do_run_migrations(connection):
|
|
70
|
+
context.configure(
|
|
71
|
+
connection=connection,
|
|
72
|
+
target_metadata=target_metadata,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
with context.begin_transaction():
|
|
76
|
+
context.run_migrations()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def run_migrations_online():
|
|
80
|
+
"""Run migrations in 'online' mode.
|
|
81
|
+
|
|
82
|
+
In this scenario we need to create an Engine
|
|
83
|
+
and associate a connection with the context.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
connectable = AsyncEngine(
|
|
87
|
+
engine_from_config(
|
|
88
|
+
config.get_section(config.config_ini_section),
|
|
89
|
+
prefix='sqlalchemy.',
|
|
90
|
+
poolclass=pool.NullPool,
|
|
91
|
+
future=True,
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
async with connectable.connect() as connection:
|
|
96
|
+
await connection.run_sync(do_run_migrations)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if context.is_offline_mode():
|
|
100
|
+
run_migrations_offline()
|
|
101
|
+
else:
|
|
102
|
+
asyncio.run(run_migrations_online())
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
${imports if imports else ""}
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision: str = ${repr(up_revision)}
|
|
16
|
+
down_revision: Union[str, None] = ${repr(down_revision)}
|
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
|
18
|
+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade() -> None:
|
|
22
|
+
${upgrades if upgrades else "pass"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def downgrade() -> None:
|
|
26
|
+
${downgrades if downgrades else "pass"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""initial
|
|
2
|
+
|
|
3
|
+
Revision ID: 64524c63b666
|
|
4
|
+
Revises:
|
|
5
|
+
Create Date: 2026-06-08 10:24:41.664877
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision: str = '64524c63b666'
|
|
16
|
+
down_revision: Union[str, None] = None
|
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
18
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade() -> None:
|
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
23
|
+
op.create_table('casbin_rule',
|
|
24
|
+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Primary key id'),
|
|
25
|
+
sa.Column('ptype', sa.String(length=255), nullable=False, comment='Policy type: p / g'),
|
|
26
|
+
sa.Column('v0', sa.String(length=255), nullable=False, comment='Role ID / User x_id'),
|
|
27
|
+
sa.Column('v1', sa.TEXT(), nullable=False, comment='API path / Role name'),
|
|
28
|
+
sa.Column('v2', sa.String(length=255), nullable=True, comment='Request method'),
|
|
29
|
+
sa.Column('v3', sa.String(length=255), nullable=True),
|
|
30
|
+
sa.Column('v4', sa.String(length=255), nullable=True),
|
|
31
|
+
sa.Column('v5', sa.String(length=255), nullable=True),
|
|
32
|
+
sa.PrimaryKeyConstraint('id')
|
|
33
|
+
)
|
|
34
|
+
op.create_index(op.f('ix_casbin_rule_id'), 'casbin_rule', ['id'], unique=False)
|
|
35
|
+
op.create_table('login_log',
|
|
36
|
+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Primary key id'),
|
|
37
|
+
sa.Column('user_x_id', sa.String(length=32), nullable=False),
|
|
38
|
+
sa.Column('email', sa.String(), nullable=False, comment='User email'),
|
|
39
|
+
sa.Column('status', sa.Integer(), nullable=False, comment='Login status (0 failed, 1 success)'),
|
|
40
|
+
sa.Column('ip', sa.String(), nullable=False, comment='Login IP address'),
|
|
41
|
+
sa.Column('country', sa.String(), nullable=True, comment='Country'),
|
|
42
|
+
sa.Column('region', sa.String(), nullable=True, comment='Region'),
|
|
43
|
+
sa.Column('city', sa.String(), nullable=True, comment='City'),
|
|
44
|
+
sa.Column('user_agent', sa.TEXT(), nullable=False, comment='User-Agent header'),
|
|
45
|
+
sa.Column('os', sa.String(), nullable=True, comment='Operating system'),
|
|
46
|
+
sa.Column('browser', sa.String(), nullable=True, comment='Browser'),
|
|
47
|
+
sa.Column('device', sa.String(), nullable=True, comment='Device'),
|
|
48
|
+
sa.Column('msg', sa.TEXT(), nullable=False, comment='Message'),
|
|
49
|
+
sa.Column('login_time', sa.DateTime(), nullable=False, comment='Login time'),
|
|
50
|
+
sa.Column('created_time', sa.DateTime(timezone=True), nullable=False, comment='Creation time'),
|
|
51
|
+
sa.PrimaryKeyConstraint('id')
|
|
52
|
+
)
|
|
53
|
+
op.create_index(op.f('ix_login_log_id'), 'login_log', ['id'], unique=False)
|
|
54
|
+
op.create_table('opera_log',
|
|
55
|
+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Primary key id'),
|
|
56
|
+
sa.Column('trace_id', sa.String(length=32), nullable=False, comment='Request Tracking ID'),
|
|
57
|
+
sa.Column('user_email', sa.String(), nullable=True, comment='User Email'),
|
|
58
|
+
sa.Column('method', sa.String(), nullable=False, comment='Request method'),
|
|
59
|
+
sa.Column('title', sa.String(), nullable=False, comment='Operating module'),
|
|
60
|
+
sa.Column('path', sa.String(), nullable=False, comment='Request path'),
|
|
61
|
+
sa.Column('ip', sa.String(), nullable=False, comment='IP address'),
|
|
62
|
+
sa.Column('country', sa.String(), nullable=True, comment='Country'),
|
|
63
|
+
sa.Column('region', sa.String(), nullable=True, comment='Region'),
|
|
64
|
+
sa.Column('city', sa.String(), nullable=True, comment='City'),
|
|
65
|
+
sa.Column('user_agent', sa.String(), nullable=False, comment='Request header'),
|
|
66
|
+
sa.Column('os', sa.String(), nullable=False, comment='Operating system'),
|
|
67
|
+
sa.Column('browser', sa.String(), nullable=False, comment='Browser (software)'),
|
|
68
|
+
sa.Column('device', sa.String(), nullable=False, comment='Device'),
|
|
69
|
+
sa.Column('args', sa.JSON(), nullable=False, comment='Request Parameters'),
|
|
70
|
+
sa.Column('status', sa.Integer(), nullable=False, comment='Operation status (0 abnormal 1 normal)'),
|
|
71
|
+
sa.Column('code', sa.String(length=20), nullable=False, comment='Operation status code'),
|
|
72
|
+
sa.Column('msg', sa.TEXT(), nullable=False, comment='Alert message'),
|
|
73
|
+
sa.Column('cost_time', sa.Float(), nullable=False, comment='Request elapsed time (ms)'),
|
|
74
|
+
sa.Column('opera_time', sa.DateTime(), nullable=False, comment='Operating time'),
|
|
75
|
+
sa.Column('created_time', sa.DateTime(timezone=True), nullable=False, comment='Creation time'),
|
|
76
|
+
sa.PrimaryKeyConstraint('id')
|
|
77
|
+
)
|
|
78
|
+
op.create_index(op.f('ix_opera_log_id'), 'opera_log', ['id'], unique=False)
|
|
79
|
+
op.create_table('role',
|
|
80
|
+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Primary key id'),
|
|
81
|
+
sa.Column('x_id', sa.String(length=32), nullable=False),
|
|
82
|
+
sa.Column('name', sa.String(), nullable=False, comment='role name'),
|
|
83
|
+
sa.Column('status', sa.Integer(), nullable=False, comment='Role status (0 deactivated 1 normal)'),
|
|
84
|
+
sa.Column('remark', sa.Text(), nullable=False, comment='note'),
|
|
85
|
+
sa.Column('created_time', sa.DateTime(timezone=True), nullable=False, comment='Creation time'),
|
|
86
|
+
sa.Column('updated_time', sa.DateTime(timezone=True), nullable=True, comment='update time'),
|
|
87
|
+
sa.PrimaryKeyConstraint('id'),
|
|
88
|
+
sa.UniqueConstraint('name'),
|
|
89
|
+
sa.UniqueConstraint('x_id')
|
|
90
|
+
)
|
|
91
|
+
op.create_index(op.f('ix_role_id'), 'role', ['id'], unique=False)
|
|
92
|
+
op.create_table('user',
|
|
93
|
+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Primary key id'),
|
|
94
|
+
sa.Column('x_id', sa.String(length=32), nullable=False),
|
|
95
|
+
sa.Column('firstname', sa.String(), nullable=True),
|
|
96
|
+
sa.Column('lastname', sa.String(), nullable=True),
|
|
97
|
+
sa.Column('phone', sa.String(), nullable=True),
|
|
98
|
+
sa.Column('email', sa.String(length=200), nullable=False),
|
|
99
|
+
sa.Column('password', sa.String(), nullable=False),
|
|
100
|
+
sa.Column('salt', sa.String(), nullable=True),
|
|
101
|
+
sa.Column('status', sa.Boolean(), server_default='1', nullable=False),
|
|
102
|
+
sa.Column('is_multi_login', sa.Boolean(), server_default='1', nullable=False),
|
|
103
|
+
sa.Column('last_login_time', sa.DateTime(timezone=True), nullable=False),
|
|
104
|
+
sa.Column('join_time', sa.DateTime(timezone=True), nullable=False),
|
|
105
|
+
sa.Column('created_time', sa.DateTime(timezone=True), nullable=False, comment='Creation time'),
|
|
106
|
+
sa.Column('updated_time', sa.DateTime(timezone=True), nullable=True, comment='update time'),
|
|
107
|
+
sa.PrimaryKeyConstraint('id'),
|
|
108
|
+
sa.UniqueConstraint('x_id')
|
|
109
|
+
)
|
|
110
|
+
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
|
|
111
|
+
op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False)
|
|
112
|
+
op.create_index(op.f('ix_user_lastname'), 'user', ['lastname'], unique=False)
|
|
113
|
+
op.create_index(op.f('ix_user_phone'), 'user', ['phone'], unique=False)
|
|
114
|
+
op.create_table('user_role',
|
|
115
|
+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Primary Key ID'),
|
|
116
|
+
sa.Column('user_id', sa.Integer(), nullable=True),
|
|
117
|
+
sa.Column('role_id', sa.Integer(), nullable=True),
|
|
118
|
+
sa.ForeignKeyConstraint(['role_id'], ['role.id'], ondelete='CASCADE'),
|
|
119
|
+
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
|
|
120
|
+
sa.PrimaryKeyConstraint('id')
|
|
121
|
+
)
|
|
122
|
+
op.create_index(op.f('ix_user_role_id'), 'user_role', ['id'], unique=True)
|
|
123
|
+
# ### end Alembic commands ###
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def downgrade() -> None:
|
|
127
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
128
|
+
op.drop_index(op.f('ix_user_role_id'), table_name='user_role')
|
|
129
|
+
op.drop_table('user_role')
|
|
130
|
+
op.drop_index(op.f('ix_user_phone'), table_name='user')
|
|
131
|
+
op.drop_index(op.f('ix_user_lastname'), table_name='user')
|
|
132
|
+
op.drop_index(op.f('ix_user_id'), table_name='user')
|
|
133
|
+
op.drop_index(op.f('ix_user_email'), table_name='user')
|
|
134
|
+
op.drop_table('user')
|
|
135
|
+
op.drop_index(op.f('ix_role_id'), table_name='role')
|
|
136
|
+
op.drop_table('role')
|
|
137
|
+
op.drop_index(op.f('ix_opera_log_id'), table_name='opera_log')
|
|
138
|
+
op.drop_table('opera_log')
|
|
139
|
+
op.drop_index(op.f('ix_login_log_id'), table_name='login_log')
|
|
140
|
+
op.drop_table('login_log')
|
|
141
|
+
op.drop_index(op.f('ix_casbin_rule_id'), table_name='casbin_rule')
|
|
142
|
+
op.drop_table('casbin_rule')
|
|
143
|
+
# ### end Alembic commands ###
|