oxutils 0.1.11__tar.gz → 0.1.12__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.
- {oxutils-0.1.11 → oxutils-0.1.12}/PKG-INFO +2 -11
- {oxutils-0.1.11 → oxutils-0.1.12}/README.md +0 -8
- {oxutils-0.1.11 → oxutils-0.1.12}/pyproject.toml +2 -3
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/__init__.py +1 -2
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/migrations/0001_initial.py +2 -2
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/models.py +2 -2
- oxutils-0.1.12/src/oxutils/logger/__init__.py +10 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/caches.py +0 -1
- oxutils-0.1.12/src/oxutils/settings.py +76 -0
- oxutils-0.1.11/src/oxutils/s3/settings.py +0 -34
- oxutils-0.1.11/src/oxutils/s3/storages.py +0 -130
- oxutils-0.1.11/src/oxutils/settings.py +0 -264
- oxutils-0.1.11/src/oxutils/users/__init__.py +0 -0
- oxutils-0.1.11/src/oxutils/users/migrations/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/apps.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/apps.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/export.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/masks.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/migrations/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/settings.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/audit/utils.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/celery/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/celery/base.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/celery/settings.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/conf.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/constants.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/context/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/context/site_name_processor.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/admin.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/apps.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/controllers.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/enums.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/migrations/0001_initial.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/migrations/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/models.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/schemas.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/tests.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/currency/utils.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/enums/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/enums/audit.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/enums/invoices.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/exceptions.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/functions.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/jwt/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/jwt/auth.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/jwt/models.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/jwt/tokens.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/jwt/utils.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/locale/fr/LC_MESSAGES/django.po +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/logger/receivers.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/logger/settings.py +0 -0
- {oxutils-0.1.11/src/oxutils/logger → oxutils-0.1.12/src/oxutils/mixins}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/mixins/base.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/mixins/schemas.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/mixins/services.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/models/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/models/base.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/models/billing.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/models/fields.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/models/invoice.py +0 -0
- {oxutils-0.1.11/src/oxutils/mixins → oxutils-0.1.12/src/oxutils/oxiliere}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/admin.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/apps.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/authorization.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/cacheops.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/checks.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/constants.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/context.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/controllers.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/enums.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/exceptions.py +0 -0
- {oxutils-0.1.11/src/oxutils/oxiliere → oxutils-0.1.12/src/oxutils/oxiliere/management}/__init__.py +0 -0
- {oxutils-0.1.11/src/oxutils/oxiliere/management → oxutils-0.1.12/src/oxutils/oxiliere/management/commands}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/management/commands/grant_tenant_owners.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/management/commands/init_oxiliere_system.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/middleware.py +0 -0
- {oxutils-0.1.11/src/oxutils/oxiliere/management/commands → oxutils-0.1.12/src/oxutils/oxiliere/migrations}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/models.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/permissions.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/schemas.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/settings.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/signals.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/tests.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/utils.py +0 -0
- {oxutils-0.1.11/src/oxutils/oxiliere/migrations → oxutils-0.1.12/src/oxutils/pagination}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/pagination/cursor.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/pdf/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/pdf/printer.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/pdf/utils.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/pdf/views.py +0 -0
- {oxutils-0.1.11/src/oxutils/pagination → oxutils-0.1.12/src/oxutils/permissions}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/actions.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/admin.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/apps.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/caches.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/checks.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/constants.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/controllers.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/exceptions.py +0 -0
- {oxutils-0.1.11/src/oxutils/permissions → oxutils-0.1.12/src/oxutils/permissions/management}/__init__.py +0 -0
- {oxutils-0.1.11/src/oxutils/permissions/management → oxutils-0.1.12/src/oxutils/permissions/management/commands}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/management/commands/load_permission_preset.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/migrations/0001_initial.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/migrations/0002_alter_grant_role.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py +0 -0
- {oxutils-0.1.11/src/oxutils/permissions/management/commands → oxutils-0.1.12/src/oxutils/permissions/migrations}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/models.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/perms.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/queryset.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/schemas.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/services.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/tests.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/utils.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/py.typed +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/types.py +0 -0
- {oxutils-0.1.11/src/oxutils/permissions/migrations → oxutils-0.1.12/src/oxutils/users}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/admin.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/apps.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/migrations/0001_initial.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py +0 -0
- {oxutils-0.1.11/src/oxutils/s3 → oxutils-0.1.12/src/oxutils/users/migrations}/__init__.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/models.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/tests.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/users/utils.py +0 -0
- {oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/utils.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oxutils
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: Production-ready utilities for Django applications in the Oxiliere ecosystem
|
|
5
|
-
Keywords: django,utilities,jwt,
|
|
5
|
+
Keywords: django,utilities,jwt,audit,logging,celery,structlog
|
|
6
6
|
Author: Edimedia Mutoke
|
|
7
7
|
Author-email: Edimedia Mutoke <eddycondor07@gmail.com>
|
|
8
8
|
License-Expression: Apache-2.0
|
|
@@ -24,7 +24,6 @@ Requires-Dist: django-celery-results>=2.6.0
|
|
|
24
24
|
Requires-Dist: django-extensions>=4.1
|
|
25
25
|
Requires-Dist: django-ninja>=1.5.0
|
|
26
26
|
Requires-Dist: django-ninja-extra>=0.30.6
|
|
27
|
-
Requires-Dist: django-storages[s3]>=1.14.6
|
|
28
27
|
Requires-Dist: django-structlog[celery]>=10.0.0
|
|
29
28
|
Requires-Dist: jwcrypto>=1.5.6
|
|
30
29
|
Requires-Dist: pydantic-settings>=2.12.0
|
|
@@ -71,7 +70,6 @@ Description-Content-Type: text/markdown
|
|
|
71
70
|
## Features
|
|
72
71
|
|
|
73
72
|
- 🔐 **JWT Authentication** - RS256 with JWKS caching
|
|
74
|
-
- 📦 **S3 Storage** - Static, media, private, and log backends
|
|
75
73
|
- 📝 **Structured Logging** - JSON logs with automatic request tracking
|
|
76
74
|
- 🔍 **Audit System** - Change tracking with S3 export
|
|
77
75
|
- ⚙️ **Celery Integration** - Pre-configured task processing
|
|
@@ -118,8 +116,6 @@ MIDDLEWARE = [
|
|
|
118
116
|
```bash
|
|
119
117
|
OXI_SERVICE_NAME=my-service
|
|
120
118
|
OXI_JWT_JWKS_URL=https://auth.example.com/.well-known/jwks.json
|
|
121
|
-
OXI_USE_STATIC_S3=True
|
|
122
|
-
OXI_STATIC_STORAGE_BUCKET_NAME=my-bucket
|
|
123
119
|
```
|
|
124
120
|
|
|
125
121
|
### 3. Usage Examples
|
|
@@ -134,10 +130,6 @@ import structlog
|
|
|
134
130
|
logger = structlog.get_logger(__name__)
|
|
135
131
|
logger.info("user_action", user_id=user_id)
|
|
136
132
|
|
|
137
|
-
# S3 Storage
|
|
138
|
-
from oxutils.s3.storages import PrivateMediaStorage
|
|
139
|
-
class Document(models.Model):
|
|
140
|
-
file = models.FileField(storage=PrivateMediaStorage())
|
|
141
133
|
|
|
142
134
|
# Model Mixins
|
|
143
135
|
from oxutils.models.base import BaseModelMixin
|
|
@@ -165,7 +157,6 @@ TEMPLATES = [{
|
|
|
165
157
|
### Core Modules
|
|
166
158
|
- **[Settings](docs/settings.md)** - Configuration reference
|
|
167
159
|
- **[JWT](docs/jwt.md)** - Authentication
|
|
168
|
-
- **[S3](docs/s3.md)** - Storage backends
|
|
169
160
|
- **[Audit](docs/audit.md)** - Change tracking
|
|
170
161
|
- **[Logging](docs/logger.md)** - Structured logs
|
|
171
162
|
- **[Mixins](docs/mixins.md)** - Model/service mixins
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
14
|
- 🔐 **JWT Authentication** - RS256 with JWKS caching
|
|
15
|
-
- 📦 **S3 Storage** - Static, media, private, and log backends
|
|
16
15
|
- 📝 **Structured Logging** - JSON logs with automatic request tracking
|
|
17
16
|
- 🔍 **Audit System** - Change tracking with S3 export
|
|
18
17
|
- ⚙️ **Celery Integration** - Pre-configured task processing
|
|
@@ -59,8 +58,6 @@ MIDDLEWARE = [
|
|
|
59
58
|
```bash
|
|
60
59
|
OXI_SERVICE_NAME=my-service
|
|
61
60
|
OXI_JWT_JWKS_URL=https://auth.example.com/.well-known/jwks.json
|
|
62
|
-
OXI_USE_STATIC_S3=True
|
|
63
|
-
OXI_STATIC_STORAGE_BUCKET_NAME=my-bucket
|
|
64
61
|
```
|
|
65
62
|
|
|
66
63
|
### 3. Usage Examples
|
|
@@ -75,10 +72,6 @@ import structlog
|
|
|
75
72
|
logger = structlog.get_logger(__name__)
|
|
76
73
|
logger.info("user_action", user_id=user_id)
|
|
77
74
|
|
|
78
|
-
# S3 Storage
|
|
79
|
-
from oxutils.s3.storages import PrivateMediaStorage
|
|
80
|
-
class Document(models.Model):
|
|
81
|
-
file = models.FileField(storage=PrivateMediaStorage())
|
|
82
75
|
|
|
83
76
|
# Model Mixins
|
|
84
77
|
from oxutils.models.base import BaseModelMixin
|
|
@@ -106,7 +99,6 @@ TEMPLATES = [{
|
|
|
106
99
|
### Core Modules
|
|
107
100
|
- **[Settings](docs/settings.md)** - Configuration reference
|
|
108
101
|
- **[JWT](docs/jwt.md)** - Authentication
|
|
109
|
-
- **[S3](docs/s3.md)** - Storage backends
|
|
110
102
|
- **[Audit](docs/audit.md)** - Change tracking
|
|
111
103
|
- **[Logging](docs/logger.md)** - Structured logs
|
|
112
104
|
- **[Mixins](docs/mixins.md)** - Model/service mixins
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "oxutils"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.12"
|
|
4
4
|
description = "Production-ready utilities for Django applications in the Oxiliere ecosystem"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -8,7 +8,7 @@ authors = [
|
|
|
8
8
|
{ name = "Edimedia Mutoke", email = "eddycondor07@gmail.com" }
|
|
9
9
|
]
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
|
-
keywords = ["django", "utilities", "jwt", "
|
|
11
|
+
keywords = ["django", "utilities", "jwt", "audit", "logging", "celery", "structlog"]
|
|
12
12
|
classifiers = [
|
|
13
13
|
"Development Status :: 4 - Beta",
|
|
14
14
|
"Framework :: Django",
|
|
@@ -30,7 +30,6 @@ dependencies = [
|
|
|
30
30
|
"django-extensions>=4.1",
|
|
31
31
|
"django-ninja>=1.5.0",
|
|
32
32
|
"django-ninja-extra>=0.30.6",
|
|
33
|
-
"django-storages[s3]>=1.14.6",
|
|
34
33
|
"django-structlog[celery]>=10.0.0",
|
|
35
34
|
"jwcrypto>=1.5.6",
|
|
36
35
|
"pydantic-settings>=2.12.0",
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
This package provides:
|
|
4
4
|
- JWT authentication with JWKS support
|
|
5
|
-
- S3 storage backends (static, media, private, logs)
|
|
6
5
|
- Structured logging with correlation IDs
|
|
7
6
|
- Audit system with S3 export
|
|
8
7
|
- Celery integration
|
|
@@ -11,7 +10,7 @@ This package provides:
|
|
|
11
10
|
- Permission management
|
|
12
11
|
"""
|
|
13
12
|
|
|
14
|
-
__version__ = "0.1.
|
|
13
|
+
__version__ = "0.1.12"
|
|
15
14
|
|
|
16
15
|
from oxutils.settings import oxi_settings
|
|
17
16
|
from oxutils.conf import UTILS_APPS, AUDIT_MIDDLEWARE
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import django.db.models.deletion
|
|
4
4
|
import oxutils.enums.audit
|
|
5
|
-
|
|
5
|
+
from oxutils.logger import get_log_storage
|
|
6
6
|
from django.db import migrations, models
|
|
7
7
|
|
|
8
8
|
|
|
@@ -22,7 +22,7 @@ class Migration(migrations.Migration):
|
|
|
22
22
|
('updated_at', models.DateTimeField(auto_now=True, help_text='Date and time when this record was last updated')),
|
|
23
23
|
('last_export_date', models.DateTimeField(null=True)),
|
|
24
24
|
('status', models.CharField(choices=[(oxutils.enums.audit.ExportStatus['FAILED'], 'Failed'), (oxutils.enums.audit.ExportStatus['PENDING'], 'Pending'), (oxutils.enums.audit.ExportStatus['SUCCESS'], 'Success')], default=oxutils.enums.audit.ExportStatus['PENDING'])),
|
|
25
|
-
('data', models.FileField(storage=
|
|
25
|
+
('data', models.FileField(storage=get_log_storage, upload_to='')),
|
|
26
26
|
('size', models.BigIntegerField()),
|
|
27
27
|
],
|
|
28
28
|
options={
|
|
@@ -3,7 +3,7 @@ from django.utils import timezone
|
|
|
3
3
|
from django.db import models, transaction
|
|
4
4
|
from oxutils.enums.audit import ExportStatus
|
|
5
5
|
from oxutils.models.base import TimestampMixin
|
|
6
|
-
from oxutils.
|
|
6
|
+
from oxutils.logger import get_log_storage
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
@@ -38,7 +38,7 @@ class LogExportState(TimestampMixin):
|
|
|
38
38
|
(ExportStatus.SUCCESS, _('Success'))
|
|
39
39
|
)
|
|
40
40
|
)
|
|
41
|
-
data = models.FileField(storage=
|
|
41
|
+
data = models.FileField(storage=get_log_storage)
|
|
42
42
|
size = models.BigIntegerField()
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from pydantic import Field
|
|
3
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'oxi_settings',
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OxUtilsSettings(BaseSettings):
|
|
15
|
+
model_config = SettingsConfigDict(
|
|
16
|
+
validate_assignment=True,
|
|
17
|
+
extra="ignore",
|
|
18
|
+
env_prefix='OXI_'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Service
|
|
22
|
+
service_name: Optional[str] = 'Oxutils'
|
|
23
|
+
site_name: Optional[str] = 'Oxiliere'
|
|
24
|
+
site_domain: Optional[str] = 'oxiliere.com'
|
|
25
|
+
multitenancy: bool = Field(False)
|
|
26
|
+
|
|
27
|
+
# Auth JWT Settings (JWT_SIGNING_KEY)
|
|
28
|
+
jwt_signing_key: Optional[str] = None
|
|
29
|
+
jwt_verifying_key: Optional[str] = None
|
|
30
|
+
jwt_jwks_url: Optional[str] = None
|
|
31
|
+
jwt_access_token_key: str = Field('access')
|
|
32
|
+
jwt_org_access_token_key: str = Field('org_access')
|
|
33
|
+
jwt_service_token_key: str = Field('service')
|
|
34
|
+
jwt_algorithm: Optional[str] = Field('RS256')
|
|
35
|
+
jwt_access_token_lifetime: int = Field(15) # minutes
|
|
36
|
+
jwt_service_token_lifetime: int = Field(3) # minutes
|
|
37
|
+
jwt_org_access_token_lifetime: int = Field(60) # minutes
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# AuditLog
|
|
41
|
+
log_access: bool = Field(False)
|
|
42
|
+
retention_delay: int = Field(7) # one week
|
|
43
|
+
|
|
44
|
+
# logger
|
|
45
|
+
log_file_path: Optional[str] = Field('logs/oxiliere.log')
|
|
46
|
+
|
|
47
|
+
def model_post_init(self, __context):
|
|
48
|
+
"""Called after model initialization to perform validation."""
|
|
49
|
+
self._validate_jwt_keys()
|
|
50
|
+
|
|
51
|
+
def _validate_jwt_keys(self):
|
|
52
|
+
"""Validate JWT key files if configured."""
|
|
53
|
+
import os
|
|
54
|
+
|
|
55
|
+
if self.jwt_signing_key:
|
|
56
|
+
if not os.path.exists(self.jwt_signing_key):
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"JWT signing key file not found at: {self.jwt_signing_key}"
|
|
59
|
+
)
|
|
60
|
+
if not os.path.isfile(self.jwt_signing_key):
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"JWT signing key path is not a file: {self.jwt_signing_key}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if self.jwt_verifying_key:
|
|
66
|
+
if not os.path.exists(self.jwt_verifying_key):
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"JWT verifying key file not found at: {self.jwt_verifying_key}"
|
|
69
|
+
)
|
|
70
|
+
if not os.path.isfile(self.jwt_verifying_key):
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"JWT verifying key path is not a file: {self.jwt_verifying_key}"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
oxi_settings = OxUtilsSettings()
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
USE_S3 = os.getenv('USE_S3') == 'TRUE'
|
|
4
|
-
|
|
5
|
-
if USE_S3:
|
|
6
|
-
# aws settings
|
|
7
|
-
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
|
|
8
|
-
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
|
|
9
|
-
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
|
|
10
|
-
AWS_DEFAULT_ACL = 'public-read'
|
|
11
|
-
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
|
|
12
|
-
AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'}
|
|
13
|
-
|
|
14
|
-
# s3 static settings
|
|
15
|
-
STATIC_LOCATION = 'static'
|
|
16
|
-
STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{STATIC_LOCATION}/'
|
|
17
|
-
STATICFILES_STORAGE = 'oxutils.s3.storages.StaticStorage'
|
|
18
|
-
|
|
19
|
-
# s3 public media settings
|
|
20
|
-
PUBLIC_MEDIA_LOCATION = 'media'
|
|
21
|
-
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{PUBLIC_MEDIA_LOCATION}/'
|
|
22
|
-
DEFAULT_FILE_STORAGE = 'oxutils.s3.storages.PublicMediaStorage'
|
|
23
|
-
|
|
24
|
-
# s3 private media settings
|
|
25
|
-
PRIVATE_MEDIA_LOCATION = 'private'
|
|
26
|
-
PRIVATE_FILE_STORAGE = 'oxutils.s3.storages.PrivateMediaStorage'
|
|
27
|
-
else:
|
|
28
|
-
STATIC_URL = '/static/'
|
|
29
|
-
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
|
30
|
-
|
|
31
|
-
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
|
|
32
|
-
|
|
33
|
-
MEDIA_URL = '/media/'
|
|
34
|
-
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
from storages.backends.s3boto3 import S3Boto3Storage
|
|
2
|
-
from oxutils.settings import oxi_settings
|
|
3
|
-
from django.core.exceptions import ImproperlyConfigured
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class StaticStorage(S3Boto3Storage):
|
|
7
|
-
def __init__(self, *args, **kwargs):
|
|
8
|
-
if not oxi_settings.use_static_s3:
|
|
9
|
-
raise ImproperlyConfigured(
|
|
10
|
-
"StaticStorage requires OXI_USE_STATIC_S3=True"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
self.access_key = oxi_settings.static_access_key_id
|
|
14
|
-
self.secret_key = oxi_settings.static_secret_access_key
|
|
15
|
-
self.bucket_name = oxi_settings.static_storage_bucket_name
|
|
16
|
-
self.custom_domain = oxi_settings.static_s3_custom_domain
|
|
17
|
-
self.location = oxi_settings.static_location
|
|
18
|
-
self.default_acl = oxi_settings.static_default_acl
|
|
19
|
-
self.file_overwrite = False
|
|
20
|
-
|
|
21
|
-
self._validate_required_fields('StaticStorage', {
|
|
22
|
-
'access_key': self.access_key,
|
|
23
|
-
'secret_key': self.secret_key,
|
|
24
|
-
'bucket_name': self.bucket_name,
|
|
25
|
-
'custom_domain': self.custom_domain,
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
super().__init__(*args, **kwargs)
|
|
29
|
-
|
|
30
|
-
@staticmethod
|
|
31
|
-
def _validate_required_fields(storage_name: str, fields: dict):
|
|
32
|
-
"""Validate that all required fields are present."""
|
|
33
|
-
missing = [name for name, value in fields.items() if not value]
|
|
34
|
-
if missing:
|
|
35
|
-
raise ImproperlyConfigured(
|
|
36
|
-
f"{storage_name} is missing required configuration: {', '.join(missing)}"
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class PublicMediaStorage(S3Boto3Storage):
|
|
41
|
-
def __init__(self, *args, **kwargs):
|
|
42
|
-
if not oxi_settings.use_default_s3:
|
|
43
|
-
raise ImproperlyConfigured(
|
|
44
|
-
"PublicMediaStorage requires OXI_USE_DEFAULT_S3=True"
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
if oxi_settings.use_static_s3_as_default:
|
|
48
|
-
self.access_key = oxi_settings.static_access_key_id
|
|
49
|
-
self.secret_key = oxi_settings.static_secret_access_key
|
|
50
|
-
self.bucket_name = oxi_settings.static_storage_bucket_name
|
|
51
|
-
self.custom_domain = oxi_settings.static_s3_custom_domain
|
|
52
|
-
else:
|
|
53
|
-
self.access_key = oxi_settings.default_s3_access_key_id
|
|
54
|
-
self.secret_key = oxi_settings.default_s3_secret_access_key
|
|
55
|
-
self.bucket_name = oxi_settings.default_s3_storage_bucket_name
|
|
56
|
-
self.custom_domain = oxi_settings.default_s3_custom_domain
|
|
57
|
-
|
|
58
|
-
self.location = oxi_settings.default_s3_location
|
|
59
|
-
self.default_acl = oxi_settings.default_s3_default_acl
|
|
60
|
-
self.file_overwrite = False
|
|
61
|
-
|
|
62
|
-
StaticStorage._validate_required_fields('PublicMediaStorage', {
|
|
63
|
-
'access_key': self.access_key,
|
|
64
|
-
'secret_key': self.secret_key,
|
|
65
|
-
'bucket_name': self.bucket_name,
|
|
66
|
-
'custom_domain': self.custom_domain,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
super().__init__(*args, **kwargs)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class PrivateMediaStorage(S3Boto3Storage):
|
|
73
|
-
def __init__(self, *args, **kwargs):
|
|
74
|
-
if not oxi_settings.use_private_s3:
|
|
75
|
-
raise ImproperlyConfigured(
|
|
76
|
-
"PrivateMediaStorage requires OXI_USE_PRIVATE_S3=True"
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
self.access_key = oxi_settings.private_s3_access_key_id
|
|
80
|
-
self.secret_key = oxi_settings.private_s3_secret_access_key
|
|
81
|
-
self.bucket_name = oxi_settings.private_s3_storage_bucket_name
|
|
82
|
-
self.custom_domain = oxi_settings.private_s3_custom_domain
|
|
83
|
-
self.location = oxi_settings.private_s3_location
|
|
84
|
-
self.default_acl = oxi_settings.private_s3_default_acl
|
|
85
|
-
self.file_overwrite = False
|
|
86
|
-
self.querystring_auth = True
|
|
87
|
-
self.querystring_expire = 3600
|
|
88
|
-
|
|
89
|
-
StaticStorage._validate_required_fields('PrivateMediaStorage', {
|
|
90
|
-
'access_key': self.access_key,
|
|
91
|
-
'secret_key': self.secret_key,
|
|
92
|
-
'bucket_name': self.bucket_name,
|
|
93
|
-
'custom_domain': self.custom_domain,
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
super().__init__(*args, **kwargs)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class LogStorage(S3Boto3Storage):
|
|
100
|
-
def __init__(self, *args, **kwargs):
|
|
101
|
-
if not oxi_settings.use_log_s3:
|
|
102
|
-
raise ImproperlyConfigured(
|
|
103
|
-
"LogStorage requires OXI_USE_LOG_S3=True"
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
if oxi_settings.use_private_s3_as_log:
|
|
107
|
-
self.access_key = oxi_settings.private_s3_access_key_id
|
|
108
|
-
self.secret_key = oxi_settings.private_s3_secret_access_key
|
|
109
|
-
self.bucket_name = oxi_settings.private_s3_storage_bucket_name
|
|
110
|
-
self.custom_domain = oxi_settings.private_s3_custom_domain
|
|
111
|
-
else:
|
|
112
|
-
self.access_key = oxi_settings.log_s3_access_key_id
|
|
113
|
-
self.secret_key = oxi_settings.log_s3_secret_access_key
|
|
114
|
-
self.bucket_name = oxi_settings.log_s3_storage_bucket_name
|
|
115
|
-
self.custom_domain = oxi_settings.log_s3_custom_domain
|
|
116
|
-
|
|
117
|
-
self.location = f'{oxi_settings.log_s3_location}/{oxi_settings.service_name}'
|
|
118
|
-
self.default_acl = oxi_settings.log_s3_default_acl
|
|
119
|
-
self.file_overwrite = False
|
|
120
|
-
self.querystring_auth = True
|
|
121
|
-
self.querystring_expire = 3600
|
|
122
|
-
|
|
123
|
-
StaticStorage._validate_required_fields('LogStorage', {
|
|
124
|
-
'access_key': self.access_key,
|
|
125
|
-
'secret_key': self.secret_key,
|
|
126
|
-
'bucket_name': self.bucket_name,
|
|
127
|
-
'custom_domain': self.custom_domain,
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
super().__init__(*args, **kwargs)
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
from pydantic import Field, model_validator
|
|
3
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
4
|
-
from django.core.exceptions import ImproperlyConfigured
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
__all__ = [
|
|
10
|
-
'oxi_settings',
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class OxUtilsSettings(BaseSettings):
|
|
16
|
-
model_config = SettingsConfigDict(
|
|
17
|
-
validate_assignment=True,
|
|
18
|
-
extra="ignore",
|
|
19
|
-
env_prefix='OXI_'
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
# Service
|
|
23
|
-
service_name: Optional[str] = 'Oxutils'
|
|
24
|
-
site_name: Optional[str] = 'Oxiliere'
|
|
25
|
-
site_domain: Optional[str] = 'oxiliere.com'
|
|
26
|
-
multitenancy: bool = Field(False)
|
|
27
|
-
|
|
28
|
-
# Auth JWT Settings (JWT_SIGNING_KEY)
|
|
29
|
-
jwt_signing_key: Optional[str] = None
|
|
30
|
-
jwt_verifying_key: Optional[str] = None
|
|
31
|
-
jwt_jwks_url: Optional[str] = None
|
|
32
|
-
jwt_access_token_key: str = Field('access')
|
|
33
|
-
jwt_org_access_token_key: str = Field('org_access')
|
|
34
|
-
jwt_service_token_key: str = Field('service')
|
|
35
|
-
jwt_algorithm: Optional[str] = Field('RS256')
|
|
36
|
-
jwt_access_token_lifetime: int = Field(15) # minutes
|
|
37
|
-
jwt_service_token_lifetime: int = Field(3) # minutes
|
|
38
|
-
jwt_org_access_token_lifetime: int = Field(60) # minutes
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# AuditLog
|
|
42
|
-
log_access: bool = Field(False)
|
|
43
|
-
retention_delay: int = Field(7) # one week
|
|
44
|
-
|
|
45
|
-
# logger
|
|
46
|
-
log_file_path: Optional[str] = Field('logs/oxiliere.log')
|
|
47
|
-
|
|
48
|
-
# Static S3
|
|
49
|
-
use_static_s3: bool = Field(False)
|
|
50
|
-
static_access_key_id: Optional[str] = None
|
|
51
|
-
static_secret_access_key: Optional[str] = None
|
|
52
|
-
static_storage_bucket_name: Optional[str] = None
|
|
53
|
-
static_default_acl: str = Field('public-read')
|
|
54
|
-
static_s3_custom_domain: Optional[str] = None
|
|
55
|
-
static_location: str = Field('static')
|
|
56
|
-
static_storage: str = Field('oxutils.s3.storages.StaticStorage')
|
|
57
|
-
|
|
58
|
-
# Default S3 for media
|
|
59
|
-
use_default_s3: bool = Field(False)
|
|
60
|
-
use_static_s3_as_default: bool = Field(False)
|
|
61
|
-
default_s3_access_key_id: Optional[str] = None
|
|
62
|
-
default_s3_secret_access_key: Optional[str] = None
|
|
63
|
-
default_s3_storage_bucket_name: Optional[str] = None
|
|
64
|
-
default_s3_default_acl: str = Field('public-read')
|
|
65
|
-
default_s3_custom_domain: Optional[str] = None
|
|
66
|
-
default_s3_location: str = Field('media')
|
|
67
|
-
default_s3_storage: str = Field('oxutils.s3.storages.PublicMediaStorage')
|
|
68
|
-
|
|
69
|
-
# Private S3 for sensible data
|
|
70
|
-
use_private_s3: bool = Field(False)
|
|
71
|
-
private_s3_access_key_id: Optional[str] = None
|
|
72
|
-
private_s3_secret_access_key: Optional[str] = None
|
|
73
|
-
private_s3_storage_bucket_name: Optional[str] = None
|
|
74
|
-
private_s3_default_acl: str = Field('private')
|
|
75
|
-
private_s3_custom_domain: Optional[str] = None
|
|
76
|
-
private_s3_location: str = Field('private')
|
|
77
|
-
private_s3_storage: str = Field('oxutils.s3.storages.PrivateMediaStorage')
|
|
78
|
-
|
|
79
|
-
# Log S3
|
|
80
|
-
use_log_s3: bool = Field(False)
|
|
81
|
-
use_private_s3_as_log: bool = Field(False)
|
|
82
|
-
log_s3_access_key_id: Optional[str] = None
|
|
83
|
-
log_s3_secret_access_key: Optional[str] = None
|
|
84
|
-
log_s3_storage_bucket_name: Optional[str] = None
|
|
85
|
-
log_s3_default_acl: str = Field('private')
|
|
86
|
-
log_s3_custom_domain: Optional[str] = None
|
|
87
|
-
log_s3_location: str = Field('oxi_logs')
|
|
88
|
-
log_s3_storage: str = Field('oxutils.s3.storages.LogStorage')
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@model_validator(mode='after')
|
|
92
|
-
def validate_s3_configurations(self):
|
|
93
|
-
"""Validate S3 and JWT configurations when enabled."""
|
|
94
|
-
# Validate JWT keys if present
|
|
95
|
-
self._validate_jwt_keys()
|
|
96
|
-
|
|
97
|
-
# Validate static S3
|
|
98
|
-
if self.use_static_s3:
|
|
99
|
-
self._validate_s3_config(
|
|
100
|
-
'static',
|
|
101
|
-
self.static_access_key_id,
|
|
102
|
-
self.static_secret_access_key,
|
|
103
|
-
self.static_storage_bucket_name,
|
|
104
|
-
self.static_s3_custom_domain
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
# Validate default S3
|
|
108
|
-
if self.use_default_s3:
|
|
109
|
-
if not self.use_static_s3_as_default:
|
|
110
|
-
self._validate_s3_config(
|
|
111
|
-
'default',
|
|
112
|
-
self.default_s3_access_key_id,
|
|
113
|
-
self.default_s3_secret_access_key,
|
|
114
|
-
self.default_s3_storage_bucket_name,
|
|
115
|
-
self.default_s3_custom_domain
|
|
116
|
-
)
|
|
117
|
-
elif not self.use_static_s3:
|
|
118
|
-
raise ValueError(
|
|
119
|
-
"OXI_USE_STATIC_S3_AS_DEFAULT requires OXI_USE_STATIC_S3 to be True"
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
# Validate private S3
|
|
123
|
-
if self.use_private_s3:
|
|
124
|
-
self._validate_s3_config(
|
|
125
|
-
'private',
|
|
126
|
-
self.private_s3_access_key_id,
|
|
127
|
-
self.private_s3_secret_access_key,
|
|
128
|
-
self.private_s3_storage_bucket_name,
|
|
129
|
-
self.private_s3_custom_domain
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
# Validate log S3
|
|
133
|
-
if self.use_log_s3:
|
|
134
|
-
if not self.use_private_s3_as_log:
|
|
135
|
-
self._validate_s3_config(
|
|
136
|
-
'log',
|
|
137
|
-
self.log_s3_access_key_id,
|
|
138
|
-
self.log_s3_secret_access_key,
|
|
139
|
-
self.log_s3_storage_bucket_name,
|
|
140
|
-
self.log_s3_custom_domain
|
|
141
|
-
)
|
|
142
|
-
elif not self.use_private_s3:
|
|
143
|
-
raise ValueError(
|
|
144
|
-
"OXI_USE_PRIVATE_S3_AS_LOG requires OXI_USE_PRIVATE_S3 to be True"
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
return self
|
|
148
|
-
|
|
149
|
-
def _validate_jwt_keys(self):
|
|
150
|
-
"""Validate JWT key files if configured."""
|
|
151
|
-
import os
|
|
152
|
-
|
|
153
|
-
if self.jwt_signing_key:
|
|
154
|
-
if not os.path.exists(self.jwt_signing_key):
|
|
155
|
-
raise ValueError(
|
|
156
|
-
f"JWT signing key file not found at: {self.jwt_signing_key}"
|
|
157
|
-
)
|
|
158
|
-
if not os.path.isfile(self.jwt_signing_key):
|
|
159
|
-
raise ValueError(
|
|
160
|
-
f"JWT signing key path is not a file: {self.jwt_signing_key}"
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
if self.jwt_verifying_key:
|
|
164
|
-
if not os.path.exists(self.jwt_verifying_key):
|
|
165
|
-
raise ValueError(
|
|
166
|
-
f"JWT verifying key file not found at: {self.jwt_verifying_key}"
|
|
167
|
-
)
|
|
168
|
-
if not os.path.isfile(self.jwt_verifying_key):
|
|
169
|
-
raise ValueError(
|
|
170
|
-
f"JWT verifying key path is not a file: {self.jwt_verifying_key}"
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
def _validate_s3_config(self, name: str, access_key: Optional[str],
|
|
174
|
-
secret_key: Optional[str], bucket: Optional[str],
|
|
175
|
-
domain: Optional[str]):
|
|
176
|
-
"""Validate required S3 configuration fields."""
|
|
177
|
-
missing_fields = []
|
|
178
|
-
if not access_key:
|
|
179
|
-
missing_fields.append(f'OXI_{name.upper()}_S3_ACCESS_KEY_ID')
|
|
180
|
-
if not secret_key:
|
|
181
|
-
missing_fields.append(f'OXI_{name.upper()}_S3_SECRET_ACCESS_KEY')
|
|
182
|
-
if not bucket:
|
|
183
|
-
missing_fields.append(f'OXI_{name.upper()}_S3_STORAGE_BUCKET_NAME')
|
|
184
|
-
if not domain:
|
|
185
|
-
missing_fields.append(f'OXI_{name.upper()}_S3_CUSTOM_DOMAIN')
|
|
186
|
-
|
|
187
|
-
if missing_fields:
|
|
188
|
-
raise ValueError(
|
|
189
|
-
f"Missing required {name} S3 configuration: {', '.join(missing_fields)}"
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
def get_static_storage_url(self) -> str:
|
|
193
|
-
"""Get static storage URL."""
|
|
194
|
-
if not self.use_static_s3:
|
|
195
|
-
raise ImproperlyConfigured(
|
|
196
|
-
"Static S3 is not enabled. Set OXI_USE_STATIC_S3=True."
|
|
197
|
-
)
|
|
198
|
-
return f'https://{self.static_s3_custom_domain}/{self.static_location}/'
|
|
199
|
-
|
|
200
|
-
def get_default_storage_url(self) -> str:
|
|
201
|
-
"""Get default storage URL."""
|
|
202
|
-
if self.use_default_s3:
|
|
203
|
-
if self.use_static_s3_as_default:
|
|
204
|
-
# Use static S3 credentials but keep default_s3 specific values (location, etc.)
|
|
205
|
-
domain = self.static_s3_custom_domain
|
|
206
|
-
else:
|
|
207
|
-
domain = self.default_s3_custom_domain
|
|
208
|
-
return f'https://{domain}/{self.default_s3_location}/'
|
|
209
|
-
|
|
210
|
-
raise ImproperlyConfigured(
|
|
211
|
-
"Default S3 is not enabled. Set OXI_USE_DEFAULT_S3=True."
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
def get_private_storage_url(self) -> str:
|
|
215
|
-
"""Get private storage URL."""
|
|
216
|
-
if not self.use_private_s3:
|
|
217
|
-
raise ImproperlyConfigured(
|
|
218
|
-
"Private S3 is not enabled. Set OXI_USE_PRIVATE_S3=True."
|
|
219
|
-
)
|
|
220
|
-
return f'https://{self.private_s3_custom_domain}/{self.private_s3_location}/'
|
|
221
|
-
|
|
222
|
-
def get_log_storage_url(self) -> str:
|
|
223
|
-
"""Get log storage URL."""
|
|
224
|
-
if not self.use_log_s3:
|
|
225
|
-
raise ImproperlyConfigured(
|
|
226
|
-
"Log S3 is not enabled. Set OXI_USE_LOG_S3=True."
|
|
227
|
-
)
|
|
228
|
-
if self.use_private_s3_as_log:
|
|
229
|
-
# Use private S3 credentials but keep log_s3 specific values (location, etc.)
|
|
230
|
-
domain = self.private_s3_custom_domain
|
|
231
|
-
else:
|
|
232
|
-
domain = self.log_s3_custom_domain
|
|
233
|
-
return f'https://{domain}/{self.log_s3_location}/{self.service_name}/'
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def write_django_settings(self, django_settings_module):
|
|
237
|
-
"""
|
|
238
|
-
Configure Django settings for S3 storages if enabled.
|
|
239
|
-
|
|
240
|
-
Sets:
|
|
241
|
-
1. STATIC_URL & STATICFILES_STORAGE (if use_static_s3)
|
|
242
|
-
2. MEDIA_URL & DEFAULT_FILE_STORAGE (if use_default_s3)
|
|
243
|
-
3. PRIVATE_MEDIA_LOCATION & PRIVATE_FILE_STORAGE (if use_private_s3)
|
|
244
|
-
|
|
245
|
-
Args:
|
|
246
|
-
django_settings_module: The Django settings module to update.
|
|
247
|
-
"""
|
|
248
|
-
# Configure static storage
|
|
249
|
-
if self.use_static_s3:
|
|
250
|
-
django_settings_module.STATIC_URL = self.get_static_storage_url()
|
|
251
|
-
django_settings_module.STATICFILES_STORAGE = self.static_storage
|
|
252
|
-
|
|
253
|
-
# Configure default/media storage
|
|
254
|
-
if self.use_default_s3:
|
|
255
|
-
django_settings_module.MEDIA_URL = self.get_default_storage_url()
|
|
256
|
-
django_settings_module.DEFAULT_FILE_STORAGE = self.default_s3_storage
|
|
257
|
-
|
|
258
|
-
# Configure private storage
|
|
259
|
-
if self.use_private_s3:
|
|
260
|
-
django_settings_module.PRIVATE_MEDIA_LOCATION = self.private_s3_location
|
|
261
|
-
django_settings_module.PRIVATE_FILE_STORAGE = self.private_s3_storage
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
oxi_settings = OxUtilsSettings()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oxutils-0.1.11/src/oxutils/oxiliere → oxutils-0.1.12/src/oxutils/oxiliere/management}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/management/commands/grant_tenant_owners.py
RENAMED
|
File without changes
|
{oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/oxiliere/management/commands/init_oxiliere_system.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oxutils-0.1.11/src/oxutils/oxiliere/migrations → oxutils-0.1.12/src/oxutils/pagination}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oxutils-0.1.11/src/oxutils/pagination → oxutils-0.1.12/src/oxutils/permissions}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oxutils-0.1.11 → oxutils-0.1.12}/src/oxutils/permissions/migrations/0002_alter_grant_role.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oxutils-0.1.11/src/oxutils/permissions/migrations → oxutils-0.1.12/src/oxutils/users}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|