core-framework 0.12.2__tar.gz → 0.12.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {core_framework-0.12.2 → core_framework-0.12.3}/PKG-INFO +1 -1
  2. {core_framework-0.12.2 → core_framework-0.12.3}/core/__init__.py +1 -1
  3. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/middleware.py +57 -18
  4. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/models.py +4 -6
  5. {core_framework-0.12.2 → core_framework-0.12.3}/core/middleware.py +5 -0
  6. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/engine.py +68 -2
  7. core_framework-0.12.3/docs/33-changelog-0.12.3.md +181 -0
  8. {core_framework-0.12.2 → core_framework-0.12.3}/pyproject.toml +1 -1
  9. {core_framework-0.12.2 → core_framework-0.12.3}/.gitignore +0 -0
  10. {core_framework-0.12.2 → core_framework-0.12.3}/README.md +0 -0
  11. {core_framework-0.12.2 → core_framework-0.12.3}/core/app.py +0 -0
  12. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/__init__.py +0 -0
  13. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/backends.py +0 -0
  14. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/base.py +0 -0
  15. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/decorators.py +0 -0
  16. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/hashers.py +0 -0
  17. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/permissions.py +0 -0
  18. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/schemas.py +0 -0
  19. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/tokens.py +0 -0
  20. {core_framework-0.12.2 → core_framework-0.12.3}/core/auth/views.py +0 -0
  21. {core_framework-0.12.2 → core_framework-0.12.3}/core/choices.py +0 -0
  22. {core_framework-0.12.2 → core_framework-0.12.3}/core/cli/__init__.py +0 -0
  23. {core_framework-0.12.2 → core_framework-0.12.3}/core/cli/main.py +0 -0
  24. {core_framework-0.12.2 → core_framework-0.12.3}/core/config.py +0 -0
  25. {core_framework-0.12.2 → core_framework-0.12.3}/core/database.py +0 -0
  26. {core_framework-0.12.2 → core_framework-0.12.3}/core/datetime.py +0 -0
  27. {core_framework-0.12.2 → core_framework-0.12.3}/core/dependencies.py +0 -0
  28. {core_framework-0.12.2 → core_framework-0.12.3}/core/deployment/__init__.py +0 -0
  29. {core_framework-0.12.2 → core_framework-0.12.3}/core/deployment/docker.py +0 -0
  30. {core_framework-0.12.2 → core_framework-0.12.3}/core/deployment/kubernetes.py +0 -0
  31. {core_framework-0.12.2 → core_framework-0.12.3}/core/deployment/pm2.py +0 -0
  32. {core_framework-0.12.2 → core_framework-0.12.3}/core/exceptions.py +0 -0
  33. {core_framework-0.12.2 → core_framework-0.12.3}/core/fields.py +0 -0
  34. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/__init__.py +0 -0
  35. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/avro.py +0 -0
  36. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/base.py +0 -0
  37. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/config.py +0 -0
  38. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/confluent/__init__.py +0 -0
  39. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/confluent/consumer.py +0 -0
  40. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/confluent/producer.py +0 -0
  41. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/decorators.py +0 -0
  42. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/kafka/__init__.py +0 -0
  43. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/kafka/admin.py +0 -0
  44. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/kafka/broker.py +0 -0
  45. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/kafka/consumer.py +0 -0
  46. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/kafka/producer.py +0 -0
  47. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/rabbitmq/__init__.py +0 -0
  48. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/rabbitmq/broker.py +0 -0
  49. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/rabbitmq/consumer.py +0 -0
  50. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/rabbitmq/producer.py +0 -0
  51. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/redis/__init__.py +0 -0
  52. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/redis/broker.py +0 -0
  53. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/redis/consumer.py +0 -0
  54. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/redis/producer.py +0 -0
  55. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/registry.py +0 -0
  56. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/topics.py +0 -0
  57. {core_framework-0.12.2 → core_framework-0.12.3}/core/messaging/workers.py +0 -0
  58. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/__init__.py +0 -0
  59. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/analyzer.py +0 -0
  60. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/cli.py +0 -0
  61. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/migration.py +0 -0
  62. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/operations.py +0 -0
  63. {core_framework-0.12.2 → core_framework-0.12.3}/core/migrations/state.py +0 -0
  64. {core_framework-0.12.2 → core_framework-0.12.3}/core/models.py +0 -0
  65. {core_framework-0.12.2 → core_framework-0.12.3}/core/permissions.py +0 -0
  66. {core_framework-0.12.2 → core_framework-0.12.3}/core/querysets.py +0 -0
  67. {core_framework-0.12.2 → core_framework-0.12.3}/core/relations.py +0 -0
  68. {core_framework-0.12.2 → core_framework-0.12.3}/core/routing.py +0 -0
  69. {core_framework-0.12.2 → core_framework-0.12.3}/core/serializers.py +0 -0
  70. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/__init__.py +0 -0
  71. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/base.py +0 -0
  72. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/config.py +0 -0
  73. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/decorators.py +0 -0
  74. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/registry.py +0 -0
  75. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/scheduler.py +0 -0
  76. {core_framework-0.12.2 → core_framework-0.12.3}/core/tasks/worker.py +0 -0
  77. {core_framework-0.12.2 → core_framework-0.12.3}/core/tenancy.py +0 -0
  78. {core_framework-0.12.2 → core_framework-0.12.3}/core/validators.py +0 -0
  79. {core_framework-0.12.2 → core_framework-0.12.3}/core/views.py +0 -0
  80. {core_framework-0.12.2 → core_framework-0.12.3}/docs/01-quickstart.md +0 -0
  81. {core_framework-0.12.2 → core_framework-0.12.3}/docs/02-viewsets.md +0 -0
  82. {core_framework-0.12.2 → core_framework-0.12.3}/docs/03-authentication.md +0 -0
  83. {core_framework-0.12.2 → core_framework-0.12.3}/docs/04-messaging.md +0 -0
  84. {core_framework-0.12.2 → core_framework-0.12.3}/docs/05-multi-service.md +0 -0
  85. {core_framework-0.12.2 → core_framework-0.12.3}/docs/06-tasks.md +0 -0
  86. {core_framework-0.12.2 → core_framework-0.12.3}/docs/07-deployment.md +0 -0
  87. {core_framework-0.12.2 → core_framework-0.12.3}/docs/08-complete-example.md +0 -0
  88. {core_framework-0.12.2 → core_framework-0.12.3}/docs/09-settings.md +0 -0
  89. {core_framework-0.12.2 → core_framework-0.12.3}/docs/10-migrations.md +0 -0
  90. {core_framework-0.12.2 → core_framework-0.12.3}/docs/11-permissions.md +0 -0
  91. {core_framework-0.12.2 → core_framework-0.12.3}/docs/12-auth-backends.md +0 -0
  92. {core_framework-0.12.2 → core_framework-0.12.3}/docs/13-validators.md +0 -0
  93. {core_framework-0.12.2 → core_framework-0.12.3}/docs/14-querysets.md +0 -0
  94. {core_framework-0.12.2 → core_framework-0.12.3}/docs/15-routing.md +0 -0
  95. {core_framework-0.12.2 → core_framework-0.12.3}/docs/16-serializers.md +0 -0
  96. {core_framework-0.12.2 → core_framework-0.12.3}/docs/17-datetime.md +0 -0
  97. {core_framework-0.12.2 → core_framework-0.12.3}/docs/18-dependencies.md +0 -0
  98. {core_framework-0.12.2 → core_framework-0.12.3}/docs/19-views.md +0 -0
  99. {core_framework-0.12.2 → core_framework-0.12.3}/docs/20-fields.md +0 -0
  100. {core_framework-0.12.2 → core_framework-0.12.3}/docs/21-tenancy.md +0 -0
  101. {core_framework-0.12.2 → core_framework-0.12.3}/docs/22-replicas.md +0 -0
  102. {core_framework-0.12.2 → core_framework-0.12.3}/docs/23-soft-delete.md +0 -0
  103. {core_framework-0.12.2 → core_framework-0.12.3}/docs/24-relations.md +0 -0
  104. {core_framework-0.12.2 → core_framework-0.12.3}/docs/25-exceptions.md +0 -0
  105. {core_framework-0.12.2 → core_framework-0.12.3}/docs/26-choices.md +0 -0
  106. {core_framework-0.12.2 → core_framework-0.12.3}/docs/27-workers.md +0 -0
  107. {core_framework-0.12.2 → core_framework-0.12.3}/docs/28-avro.md +0 -0
  108. {core_framework-0.12.2 → core_framework-0.12.3}/docs/29-topics.md +0 -0
  109. {core_framework-0.12.2 → core_framework-0.12.3}/docs/30-changelog-0.12.2.md +0 -0
  110. {core_framework-0.12.2 → core_framework-0.12.3}/docs/31-middleware.md +0 -0
  111. {core_framework-0.12.2 → core_framework-0.12.3}/docs/32-migration-guide-0.12.2.md +0 -0
  112. {core_framework-0.12.2 → core_framework-0.12.3}/docs/99-faq-troubleshooting.md +0 -0
  113. {core_framework-0.12.2 → core_framework-0.12.3}/docs/GUIDE.md +0 -0
  114. {core_framework-0.12.2 → core_framework-0.12.3}/docs/README.md +0 -0
  115. {core_framework-0.12.2 → core_framework-0.12.3}/example/__init__.py +0 -0
  116. {core_framework-0.12.2 → core_framework-0.12.3}/example/app.py +0 -0
  117. {core_framework-0.12.2 → core_framework-0.12.3}/example/auth.py +0 -0
  118. {core_framework-0.12.2 → core_framework-0.12.3}/example/models.py +0 -0
  119. {core_framework-0.12.2 → core_framework-0.12.3}/example/schemas.py +0 -0
  120. {core_framework-0.12.2 → core_framework-0.12.3}/example/views.py +0 -0
  121. {core_framework-0.12.2 → core_framework-0.12.3}/libs/__init__.py +0 -0
  122. {core_framework-0.12.2 → core_framework-0.12.3}/main.py +0 -0
  123. {core_framework-0.12.2 → core_framework-0.12.3}/tests/__init__.py +0 -0
  124. {core_framework-0.12.2 → core_framework-0.12.3}/tests/conftest.py +0 -0
  125. {core_framework-0.12.2 → core_framework-0.12.3}/tests/test_models.py +0 -0
  126. {core_framework-0.12.2 → core_framework-0.12.3}/tests/test_querysets.py +0 -0
  127. {core_framework-0.12.2 → core_framework-0.12.3}/tests/test_serializers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-framework
3
- Version: 0.12.2
3
+ Version: 0.12.3
4
4
  Summary: Core Framework - Django-inspired, FastAPI-powered. Alta performance, baixo acoplamento, produtividade extrema.
5
5
  Project-URL: Homepage, https://github.com/SorPuti/core-framework
6
6
  Project-URL: Documentation, https://github.com/SorPuti/core-framework#readme
@@ -278,7 +278,7 @@ from core.exceptions import (
278
278
  MissingDependency,
279
279
  )
280
280
 
281
- __version__ = "0.12.2"
281
+ __version__ = "0.12.3"
282
282
  __all__ = [
283
283
  # Models
284
284
  "Model",
@@ -182,26 +182,65 @@ class AuthenticationMiddleware(BaseHTTPMiddleware):
182
182
  if User is None:
183
183
  return None
184
184
 
185
- # Get database session
186
- from core.database import get_read_session
185
+ # Bug #3 & #4 Fix: Get database session correctly
186
+ # The middleware runs outside FastAPI DI context, so we need to
187
+ # handle database session creation carefully
188
+ db = await self._get_db_session()
189
+ if db is None:
190
+ return None
187
191
 
188
- async for db in get_read_session():
189
- try:
190
- # Convert user_id to correct type
191
- user_id_converted = self._convert_user_id(user_id, User)
192
-
193
- user = await User.objects.using(db).filter(id=user_id_converted).first()
194
-
195
- if user is None:
196
- return None
197
-
198
- # Check if user is active
199
- if hasattr(user, "is_active") and not user.is_active:
200
- return None
201
-
202
- return user
203
- except Exception:
192
+ try:
193
+ # Convert user_id to correct type
194
+ user_id_converted = self._convert_user_id(user_id, User)
195
+
196
+ user = await User.objects.using(db).filter(id=user_id_converted).first()
197
+
198
+ if user is None:
204
199
  return None
200
+
201
+ # Check if user is active
202
+ if hasattr(user, "is_active") and not user.is_active:
203
+ return None
204
+
205
+ return user
206
+ except Exception:
207
+ return None
208
+ finally:
209
+ await db.close()
210
+
211
+ async def _get_db_session(self) -> Any | None:
212
+ """
213
+ Get a database session for authentication.
214
+
215
+ Bug #3 & #4 Fix: Handles both initialized and uninitialized database states.
216
+ Creates session directly from engine if normal path fails.
217
+
218
+ Returns:
219
+ AsyncSession or None if database not available
220
+ """
221
+ # Try 1: Use the standard get_read_session (if database is initialized)
222
+ try:
223
+ from core.database import get_read_session, _read_session_factory
224
+
225
+ if _read_session_factory is not None:
226
+ return _read_session_factory()
227
+ except (RuntimeError, ImportError):
228
+ pass
229
+
230
+ # Try 2: Create session from settings (lazy initialization)
231
+ try:
232
+ from core.config import get_settings
233
+ from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
234
+
235
+ settings = get_settings()
236
+ db_url = getattr(settings, 'database_read_url', None) or getattr(settings, 'database_url', None)
237
+
238
+ if db_url:
239
+ engine = create_async_engine(db_url, echo=False)
240
+ session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
241
+ return session_factory()
242
+ except Exception:
243
+ pass
205
244
 
206
245
  return None
207
246
 
@@ -27,12 +27,14 @@ Uso:
27
27
  from __future__ import annotations
28
28
 
29
29
  from typing import Any, ClassVar, TYPE_CHECKING
30
+ from uuid import UUID
30
31
 
31
32
  from sqlalchemy import Table, Column, Integer, ForeignKey, inspect
32
33
  from sqlalchemy.orm import Mapped, relationship, declared_attr
33
34
  from sqlalchemy.dialects.postgresql import UUID as PG_UUID
34
35
 
35
36
  from core.models import Model, Field
37
+ from core.fields import AdvancedField
36
38
  from core.auth.base import get_password_hasher, get_auth_config
37
39
  from core.datetime import timezone, DateTime
38
40
 
@@ -69,7 +71,6 @@ def _get_pk_column_type(model_class: type) -> type:
69
71
  from sqlalchemy import Integer, BigInteger, String
70
72
  from sqlalchemy.dialects.postgresql import UUID as PG_UUID
71
73
  from sqlalchemy import Uuid
72
- import uuid
73
74
 
74
75
  # Verifica cache primeiro
75
76
  cache_key = f"{model_class.__module__}.{model_class.__name__}"
@@ -790,12 +791,9 @@ class AbstractUUIDUser(AbstractUser):
790
791
 
791
792
  __abstract__ = True
792
793
 
793
- # Importa aqui para evitar circular import
794
- from core.fields import AdvancedField
795
- from uuid import UUID as UUIDType
796
-
797
794
  # Override: usa UUID como PK
798
- id: Mapped[UUIDType] = AdvancedField.uuid_pk()
795
+ # UUID importado no topo do módulo para resolução correta de tipos
796
+ id: Mapped[UUID] = AdvancedField.uuid_pk()
799
797
 
800
798
 
801
799
  # =============================================================================
@@ -736,11 +736,16 @@ class SecurityHeadersMiddleware(BaseMiddleware):
736
736
  # =============================================================================
737
737
 
738
738
  _builtin_middlewares.update({
739
+ # Pre-built middlewares from this module
739
740
  "timing": "core.middleware.TimingMiddleware",
740
741
  "request_id": "core.middleware.RequestIDMiddleware",
741
742
  "logging": "core.middleware.LoggingMiddleware",
742
743
  "maintenance": "core.middleware.MaintenanceModeMiddleware",
743
744
  "security_headers": "core.middleware.SecurityHeadersMiddleware",
745
+ # Auth middlewares (ensure they're registered even if initial dict failed)
746
+ "auth": "core.auth.middleware.AuthenticationMiddleware",
747
+ "authentication": "core.auth.middleware.AuthenticationMiddleware",
748
+ "optional_auth": "core.auth.middleware.OptionalAuthenticationMiddleware",
744
749
  })
745
750
 
746
751
 
@@ -255,6 +255,71 @@ class MigrationEngine:
255
255
  index=col.index,
256
256
  )
257
257
 
258
+ def _topological_sort_tables(self, tables: list[TableState]) -> list[TableState]:
259
+ """
260
+ Bug #5 Fix: Ordena tabelas em ordem topológica baseada em FKs.
261
+
262
+ Tabelas referenciadas por FKs são criadas ANTES das que as referenciam.
263
+
264
+ Args:
265
+ tables: Lista de TableState a ordenar
266
+
267
+ Returns:
268
+ Lista ordenada topologicamente
269
+ """
270
+ if not tables:
271
+ return []
272
+
273
+ # Mapeia nome -> tabela
274
+ table_map = {t.name: t for t in tables}
275
+
276
+ # Constrói grafo de dependências
277
+ # dependencias[A] = {B, C} significa A depende de B e C (A tem FK para B e C)
278
+ dependencies: dict[str, set[str]] = {t.name: set() for t in tables}
279
+
280
+ for table in tables:
281
+ for fk in table.foreign_keys:
282
+ ref_table = fk.references_table
283
+ # Só adiciona dependência se a tabela referenciada também está sendo criada
284
+ # (tabelas já existentes não precisam ser consideradas)
285
+ if ref_table in table_map:
286
+ dependencies[table.name].add(ref_table)
287
+
288
+ # Algoritmo de Kahn para ordenação topológica
289
+ result = []
290
+
291
+ # Encontra tabelas sem dependências (grau de entrada 0)
292
+ no_deps = [name for name, deps in dependencies.items() if not deps]
293
+
294
+ while no_deps:
295
+ # Remove uma tabela sem dependências
296
+ name = no_deps.pop(0)
297
+ result.append(table_map[name])
298
+
299
+ # Remove esta tabela das dependências de outras
300
+ for other_name, deps in dependencies.items():
301
+ if name in deps:
302
+ deps.remove(name)
303
+ # Se não tem mais dependências, adiciona à fila
304
+ if not deps and other_name not in [t.name for t in result]:
305
+ no_deps.append(other_name)
306
+
307
+ # Verifica ciclo (se sobrou alguma tabela com dependências)
308
+ remaining = [t for t in tables if t not in result]
309
+ if remaining:
310
+ # Há um ciclo - adiciona as tabelas restantes no final
311
+ # (o banco vai falhar, mas pelo menos o erro será claro)
312
+ import warnings
313
+ cycle_tables = [t.name for t in remaining]
314
+ warnings.warn(
315
+ f"Circular FK dependency detected involving tables: {cycle_tables}. "
316
+ "Migration may fail. Consider breaking the cycle with nullable FKs.",
317
+ RuntimeWarning,
318
+ )
319
+ result.extend(remaining)
320
+
321
+ return result
322
+
258
323
  def _diff_to_operations(self, diff: SchemaDiff) -> list[Operation]:
259
324
  """Converte SchemaDiff em lista de operações."""
260
325
  from core.migrations.operations import CreateEnum, DropEnum, AlterEnum
@@ -284,8 +349,9 @@ class MigrationEngine:
284
349
  new_values=new_enum.values,
285
350
  ))
286
351
 
287
- # 3. Criar tabelas
288
- for table in diff.tables_to_create:
352
+ # 3. Criar tabelas (BUG #5 FIX: em ordem topológica)
353
+ sorted_tables = self._topological_sort_tables(diff.tables_to_create)
354
+ for table in sorted_tables:
289
355
  columns = [self._column_state_to_def(col) for col in table.columns.values()]
290
356
  foreign_keys = [
291
357
  ForeignKeyDef(
@@ -0,0 +1,181 @@
1
+ # Changelog v0.12.3
2
+
3
+ **Data de Release:** 03/02/2026
4
+
5
+ Esta versão corrige 5 bugs críticos encontrados na v0.12.2 que impediam o funcionamento do `AuthenticationMiddleware` e causavam erros em models com UUID.
6
+
7
+ ---
8
+
9
+ ## Bugs Corrigidos
10
+
11
+ ### Bug #1: `AbstractUUIDUser` usa tipo não resolvível
12
+ **Arquivo:** `core/auth/models.py`
13
+
14
+ **Problema:** A classe `AbstractUUIDUser` definia o campo `id` com um tipo `UUIDType` que não era importado corretamente no escopo do módulo, causando erro de resolução de tipo pelo SQLAlchemy.
15
+
16
+ **Antes:**
17
+ ```python
18
+ class AbstractUUIDUser(AbstractUser):
19
+ # Import dentro da classe (problemático)
20
+ from uuid import UUID as UUIDType
21
+ id: Mapped[UUIDType] = AdvancedField.uuid_pk()
22
+ ```
23
+
24
+ **Erro:**
25
+ ```
26
+ MappedAnnotationError: Could not resolve all types within mapped annotation: "Mapped[UUIDType]"
27
+ ```
28
+
29
+ **Depois:**
30
+ ```python
31
+ # Imports no topo do módulo
32
+ from uuid import UUID
33
+ from core.fields import AdvancedField
34
+
35
+ class AbstractUUIDUser(AbstractUser):
36
+ id: Mapped[UUID] = AdvancedField.uuid_pk()
37
+ ```
38
+
39
+ ---
40
+
41
+ ### Bug #2: Shortcut `"auth"` não registrado nos middleware shortcuts
42
+ **Arquivo:** `core/middleware.py`
43
+
44
+ **Problema:** O shortcut `"auth"` era definido no início do arquivo, mas podia falhar dependendo da ordem de carregamento dos módulos.
45
+
46
+ **Correção:** Adicionados os shortcuts de auth também no `_builtin_middlewares.update()` para garantir registro mesmo em casos de edge.
47
+
48
+ ```python
49
+ _builtin_middlewares.update({
50
+ # ... outros middlewares ...
51
+ "auth": "core.auth.middleware.AuthenticationMiddleware",
52
+ "authentication": "core.auth.middleware.AuthenticationMiddleware",
53
+ "optional_auth": "core.auth.middleware.OptionalAuthenticationMiddleware",
54
+ })
55
+ ```
56
+
57
+ ---
58
+
59
+ ### Bug #3: `AuthenticationMiddleware` usa `async for` incorretamente
60
+ **Arquivo:** `core/auth/middleware.py`
61
+
62
+ **Problema:** O middleware usava `async for db in get_read_session()` mas `get_read_session()` é uma coroutine que retorna `AsyncSession` diretamente, não um async generator.
63
+
64
+ **Antes:**
65
+ ```python
66
+ async for db in get_read_session(): # ERRADO!
67
+ # ...
68
+ ```
69
+
70
+ **Erro:**
71
+ ```
72
+ RuntimeWarning: coroutine 'get_read_session' was never awaited
73
+ ```
74
+
75
+ **Depois:**
76
+ ```python
77
+ db = await self._get_db_session()
78
+ try:
79
+ # ... usar db ...
80
+ finally:
81
+ await db.close()
82
+ ```
83
+
84
+ ---
85
+
86
+ ### Bug #4: `get_read_session()` falha fora do contexto FastAPI
87
+ **Arquivo:** `core/auth/middleware.py`
88
+
89
+ **Problema:** O middleware executa antes do lifecycle de dependency injection do FastAPI, então `init_replicas()` pode não ter sido chamado, causando erro de "Database not initialized".
90
+
91
+ **Erro:**
92
+ ```
93
+ RuntimeError: Database not initialized. Call init_replicas() first.
94
+ ```
95
+
96
+ **Correção:** Implementado método `_get_db_session()` no middleware que:
97
+ 1. Tenta usar o session factory padrão se já inicializado
98
+ 2. Como fallback, cria uma sessão diretamente a partir das settings
99
+
100
+ ```python
101
+ async def _get_db_session(self) -> AsyncSession | None:
102
+ # Try 1: Use standard factory if initialized
103
+ try:
104
+ if _read_session_factory is not None:
105
+ return _read_session_factory()
106
+ except RuntimeError:
107
+ pass
108
+
109
+ # Try 2: Create from settings (lazy init)
110
+ try:
111
+ settings = get_settings()
112
+ db_url = settings.database_url
113
+ engine = create_async_engine(db_url)
114
+ session_factory = async_sessionmaker(engine)
115
+ return session_factory()
116
+ except Exception:
117
+ pass
118
+
119
+ return None
120
+ ```
121
+
122
+ ---
123
+
124
+ ### Bug #5: Ordenação topológica incorreta nas migrations
125
+ **Arquivo:** `core/migrations/engine.py`
126
+
127
+ **Problema:** Tabelas com foreign keys eram criadas antes das tabelas que elas referenciam, causando erro de "relation does not exist".
128
+
129
+ **Erro:**
130
+ ```
131
+ asyncpg.exceptions.UndefinedTableError: relation "workspaces" does not exist
132
+ [SQL: CREATE TABLE "domains" (..., FOREIGN KEY ("workspace_id") REFERENCES "workspaces" ...)]
133
+ ```
134
+
135
+ **Correção:** Implementado algoritmo de ordenação topológica (Kahn's algorithm) no método `_topological_sort_tables()` que:
136
+ 1. Constrói grafo de dependências baseado em FKs
137
+ 2. Ordena tabelas garantindo que referenciadas sejam criadas primeiro
138
+ 3. Detecta e alerta sobre dependências circulares
139
+
140
+ ```python
141
+ def _topological_sort_tables(self, tables: list[TableState]) -> list[TableState]:
142
+ # Constrói grafo de dependências
143
+ dependencies = {t.name: set() for t in tables}
144
+ for table in tables:
145
+ for fk in table.foreign_keys:
146
+ if fk.references_table in table_map:
147
+ dependencies[table.name].add(fk.references_table)
148
+
149
+ # Kahn's algorithm para ordenação topológica
150
+ # ... ordena tabelas por dependências ...
151
+ return sorted_tables
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Resumo das Mudanças
157
+
158
+ | Bug | Arquivo | Correção |
159
+ |-----|---------|----------|
160
+ | #1 | `core/auth/models.py` | Import de `UUID` movido para nível do módulo |
161
+ | #2 | `core/middleware.py` | Shortcuts de auth adicionados no `.update()` |
162
+ | #3 | `core/auth/middleware.py` | Removido `async for`, usa `await` corretamente |
163
+ | #4 | `core/auth/middleware.py` | Novo método `_get_db_session()` com lazy init |
164
+ | #5 | `core/migrations/engine.py` | Implementada ordenação topológica de tabelas |
165
+
166
+ ---
167
+
168
+ ## Como Atualizar
169
+
170
+ ```bash
171
+ pip install --upgrade core-framework==0.12.3
172
+ ```
173
+
174
+ ## Impacto
175
+
176
+ Com estas correções:
177
+ - `AbstractUUIDUser` funciona corretamente para modelos com UUID PK
178
+ - `AuthenticationMiddleware` funciona out-of-the-box
179
+ - `request.state.user` é populado corretamente
180
+ - Endpoint `/auth/me` funciona sem workarounds
181
+ - Migrations criam tabelas na ordem correta de dependências
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "core-framework"
3
- version = "0.12.2"
3
+ version = "0.12.3"
4
4
  description = "Core Framework - Django-inspired, FastAPI-powered. Alta performance, baixo acoplamento, produtividade extrema."
5
5
  requires-python = ">=3.10,<4.0"
6
6
  readme = "README.md"
File without changes