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.
Files changed (141) hide show
  1. shaapi/__init__.py +3 -0
  2. shaapi/cli.py +97 -0
  3. shaapi/generator.py +114 -0
  4. shaapi/template/.dockerignore +37 -0
  5. shaapi/template/.env.template +59 -0
  6. shaapi/template/.gitattributes +12 -0
  7. shaapi/template/.gitignore +170 -0
  8. shaapi/template/.gitlab-ci.yml +89 -0
  9. shaapi/template/Dockerfile +59 -0
  10. shaapi/template/LICENSE +21 -0
  11. shaapi/template/README.md +206 -0
  12. shaapi/template/backend/.gitignore +164 -0
  13. shaapi/template/backend/__init__.py +0 -0
  14. shaapi/template/backend/alembic/README +1 -0
  15. shaapi/template/backend/alembic/env.py +102 -0
  16. shaapi/template/backend/alembic/script.py.mako +26 -0
  17. shaapi/template/backend/alembic/versions/2026_06_08_1024-64524c63b666_initial.py +143 -0
  18. shaapi/template/backend/alembic.ini +117 -0
  19. shaapi/template/backend/app/__init__.py +55 -0
  20. shaapi/template/backend/app/admin/__init__.py +1 -0
  21. shaapi/template/backend/app/admin/api/v1/__init__.py +0 -0
  22. shaapi/template/backend/app/admin/api/v1/auth.py +59 -0
  23. shaapi/template/backend/app/admin/api/v1/casbin.py +218 -0
  24. shaapi/template/backend/app/admin/api/v1/login_log.py +63 -0
  25. shaapi/template/backend/app/admin/api/v1/opera_log.py +61 -0
  26. shaapi/template/backend/app/admin/api/v1/role.py +108 -0
  27. shaapi/template/backend/app/admin/api/v1/user.py +47 -0
  28. shaapi/template/backend/app/admin/schema/casbin_rule.py +45 -0
  29. shaapi/template/backend/app/admin/schema/login_log.py +36 -0
  30. shaapi/template/backend/app/admin/schema/opera_log.py +43 -0
  31. shaapi/template/backend/app/admin/schema/role.py +36 -0
  32. shaapi/template/backend/app/admin/schema/sso.py +37 -0
  33. shaapi/template/backend/app/admin/schema/token.py +74 -0
  34. shaapi/template/backend/app/admin/schema/user.py +93 -0
  35. shaapi/template/backend/app/admin/service/auth_service.py +233 -0
  36. shaapi/template/backend/app/admin/service/casbin_service.py +135 -0
  37. shaapi/template/backend/app/admin/service/login_log_service.py +62 -0
  38. shaapi/template/backend/app/admin/service/opera_log_service.py +31 -0
  39. shaapi/template/backend/app/admin/service/role_service.py +79 -0
  40. shaapi/template/backend/app/admin/service/secure_token_service.py +60 -0
  41. shaapi/template/backend/app/admin/service/user_service.py +153 -0
  42. shaapi/template/backend/app/api.py +11 -0
  43. shaapi/template/backend/common/__init__.py +0 -0
  44. shaapi/template/backend/common/cloud_storage/__init__.py +11 -0
  45. shaapi/template/backend/common/cloud_storage/cloud_storage.py +180 -0
  46. shaapi/template/backend/common/dataclasses.py +52 -0
  47. shaapi/template/backend/common/email_conf/email.py +105 -0
  48. shaapi/template/backend/common/enums.py +144 -0
  49. shaapi/template/backend/common/exception/__init__.py +0 -0
  50. shaapi/template/backend/common/exception/errors.py +87 -0
  51. shaapi/template/backend/common/exception/exception_handler.py +280 -0
  52. shaapi/template/backend/common/log.py +123 -0
  53. shaapi/template/backend/common/model.py +68 -0
  54. shaapi/template/backend/common/pagination.py +83 -0
  55. shaapi/template/backend/common/response/__init__.py +0 -0
  56. shaapi/template/backend/common/response/response_code.py +158 -0
  57. shaapi/template/backend/common/response/response_schema.py +110 -0
  58. shaapi/template/backend/common/schema.py +144 -0
  59. shaapi/template/backend/common/security/jwt.py +203 -0
  60. shaapi/template/backend/common/security/rbac.py +98 -0
  61. shaapi/template/backend/common/security/sec_token.py +6 -0
  62. shaapi/template/backend/common/socketio/action.py +11 -0
  63. shaapi/template/backend/common/socketio/server.py +50 -0
  64. shaapi/template/backend/common/sso/base.py +69 -0
  65. shaapi/template/backend/common/sso/google.py +127 -0
  66. shaapi/template/backend/core/conf.py +208 -0
  67. shaapi/template/backend/core/path_conf.py +24 -0
  68. shaapi/template/backend/core/registrar.py +195 -0
  69. shaapi/template/backend/crud/__init__.py +1 -0
  70. shaapi/template/backend/crud/crud_base.py +35 -0
  71. shaapi/template/backend/crud/crud_casbin.py +46 -0
  72. shaapi/template/backend/crud/crud_login_log.py +58 -0
  73. shaapi/template/backend/crud/crud_opera_log.py +58 -0
  74. shaapi/template/backend/crud/crud_role.py +128 -0
  75. shaapi/template/backend/crud/crud_user.py +267 -0
  76. shaapi/template/backend/database/__init__.py +0 -0
  77. shaapi/template/backend/database/db_postgres.py +125 -0
  78. shaapi/template/backend/database/db_redis.py +62 -0
  79. shaapi/template/backend/entrypoint-api.sh +19 -0
  80. shaapi/template/backend/lang/en/app.py +18 -0
  81. shaapi/template/backend/lang/en/auth.py +10 -0
  82. shaapi/template/backend/lang/fr/app.py +18 -0
  83. shaapi/template/backend/lang/fr/auth.py +10 -0
  84. shaapi/template/backend/main.py +54 -0
  85. shaapi/template/backend/middleware/__init__.py +1 -0
  86. shaapi/template/backend/middleware/access_middleware.py +19 -0
  87. shaapi/template/backend/middleware/i18n_middleware.py +19 -0
  88. shaapi/template/backend/middleware/jwt_auth_middleware.py +73 -0
  89. shaapi/template/backend/middleware/opera_log_middleware.py +179 -0
  90. shaapi/template/backend/middleware/state_middleware.py +26 -0
  91. shaapi/template/backend/models/__init__.py +10 -0
  92. shaapi/template/backend/models/associations.py +20 -0
  93. shaapi/template/backend/models/casbin_rule.py +30 -0
  94. shaapi/template/backend/models/login_log.py +28 -0
  95. shaapi/template/backend/models/opera_log.py +36 -0
  96. shaapi/template/backend/models/role.py +27 -0
  97. shaapi/template/backend/models/user.py +30 -0
  98. shaapi/template/backend/seeder/json/admin.json +15 -0
  99. shaapi/template/backend/seeder/json/user.json +15 -0
  100. shaapi/template/backend/seeder/run.py +34 -0
  101. shaapi/template/backend/static/ip2region.xdb +0 -0
  102. shaapi/template/backend/templates/build/meet.html +169 -0
  103. shaapi/template/backend/templates/build/new_account.html +373 -0
  104. shaapi/template/backend/templates/build/reset-password.html +170 -0
  105. shaapi/template/backend/templates/build/test_email.html +25 -0
  106. shaapi/template/backend/templates/build/welcome-one-1.html +160 -0
  107. shaapi/template/backend/templates/build/welcome-one.html +178 -0
  108. shaapi/template/backend/templates/build/welcome-two.html +234 -0
  109. shaapi/template/backend/templates/index.html +0 -0
  110. shaapi/template/backend/templates/src/new_account.mjml +15 -0
  111. shaapi/template/backend/templates/src/reset_password.mjml +19 -0
  112. shaapi/template/backend/templates/src/test_email.mjml +11 -0
  113. shaapi/template/backend/templates/ws/ws.html +70 -0
  114. shaapi/template/backend/utils/demo_site.py +18 -0
  115. shaapi/template/backend/utils/encrypt.py +108 -0
  116. shaapi/template/backend/utils/health_check.py +34 -0
  117. shaapi/template/backend/utils/prometheus.py +135 -0
  118. shaapi/template/backend/utils/request_parse.py +110 -0
  119. shaapi/template/backend/utils/serializers.py +75 -0
  120. shaapi/template/backend/utils/timezone.py +51 -0
  121. shaapi/template/backend/utils/trace_id.py +7 -0
  122. shaapi/template/backend/utils/translator.py +28 -0
  123. shaapi/template/devops/scripts/deploy.sh +7 -0
  124. shaapi/template/devops/scripts/setup_env.sh +62 -0
  125. shaapi/template/docker-compose.monitoring.yml +63 -0
  126. shaapi/template/docker-compose.override.yml +12 -0
  127. shaapi/template/docker-compose.yml +90 -0
  128. shaapi/template/docker-run.sh +99 -0
  129. shaapi/template/etc/dashboards/fastapi-observability.json +1044 -0
  130. shaapi/template/etc/dashboards.yaml +10 -0
  131. shaapi/template/etc/grafana/datasource.yml +79 -0
  132. shaapi/template/etc/prometheus/prometheus.yml +52 -0
  133. shaapi/template/package-lock.json +2102 -0
  134. shaapi/template/package.json +16 -0
  135. shaapi/template/pyproject.toml +78 -0
  136. shaapi/template/uv.lock +2866 -0
  137. shaapi-0.1.0.dist-info/METADATA +92 -0
  138. shaapi-0.1.0.dist-info/RECORD +141 -0
  139. shaapi-0.1.0.dist-info/WHEEL +4 -0
  140. shaapi-0.1.0.dist-info/entry_points.txt +2 -0
  141. 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 ###