app-platform 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. app_platform-0.2.0/PKG-INFO +164 -0
  2. app_platform-0.2.0/README.md +127 -0
  3. app_platform-0.2.0/app_platform/__init__.py +123 -0
  4. app_platform-0.2.0/app_platform/auth/__init__.py +21 -0
  5. app_platform-0.2.0/app_platform/auth/dependencies.py +28 -0
  6. app_platform-0.2.0/app_platform/auth/google.py +50 -0
  7. app_platform-0.2.0/app_platform/auth/protocols.py +50 -0
  8. app_platform-0.2.0/app_platform/auth/service.py +180 -0
  9. app_platform-0.2.0/app_platform/auth/stores.py +336 -0
  10. app_platform-0.2.0/app_platform/db/__init__.py +50 -0
  11. app_platform-0.2.0/app_platform/db/client_base.py +62 -0
  12. app_platform-0.2.0/app_platform/db/exceptions.py +257 -0
  13. app_platform-0.2.0/app_platform/db/migration.py +62 -0
  14. app_platform-0.2.0/app_platform/db/pool.py +99 -0
  15. app_platform-0.2.0/app_platform/db/session.py +67 -0
  16. app_platform-0.2.0/app_platform/gateway/__init__.py +6 -0
  17. app_platform-0.2.0/app_platform/gateway/models.py +27 -0
  18. app_platform-0.2.0/app_platform/gateway/proxy.py +287 -0
  19. app_platform-0.2.0/app_platform/gateway/session.py +108 -0
  20. app_platform-0.2.0/app_platform/logging/__init__.py +45 -0
  21. app_platform-0.2.0/app_platform/logging/core.py +665 -0
  22. app_platform-0.2.0/app_platform/logging/decorators.py +180 -0
  23. app_platform-0.2.0/app_platform/middleware/__init__.py +63 -0
  24. app_platform-0.2.0/app_platform/middleware/cors.py +28 -0
  25. app_platform-0.2.0/app_platform/middleware/error_handlers.py +126 -0
  26. app_platform-0.2.0/app_platform/middleware/rate_limiter.py +114 -0
  27. app_platform-0.2.0/app_platform/middleware/sessions.py +26 -0
  28. app_platform-0.2.0/app_platform/py.typed +1 -0
  29. app_platform-0.2.0/app_platform/pyproject.toml +49 -0
  30. app_platform-0.2.0/pyproject.toml +49 -0
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: app-platform
3
+ Version: 0.2.0
4
+ Summary: Generic web app infrastructure: PostgreSQL pooling, structured logging, rate limiting, FastAPI middleware, auth service, gateway proxy
5
+ Project-URL: Homepage, https://github.com/henrysouchien/app-platform
6
+ Project-URL: Repository, https://github.com/henrysouchien/app-platform
7
+ Author: Henry Chien
8
+ License-Expression: MIT
9
+ Classifier: Framework :: FastAPI
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Topic :: Internet :: WWW/HTTP
15
+ Requires-Python: >=3.11
16
+ Requires-Dist: psycopg2-binary>=2.9
17
+ Provides-Extra: all
18
+ Requires-Dist: fastapi>=0.100; extra == 'all'
19
+ Requires-Dist: google-auth>=2.0; extra == 'all'
20
+ Requires-Dist: httpx>=0.24; extra == 'all'
21
+ Requires-Dist: itsdangerous>=2.0; extra == 'all'
22
+ Requires-Dist: requests>=2.20; extra == 'all'
23
+ Requires-Dist: slowapi>=0.1.9; extra == 'all'
24
+ Requires-Dist: starlette; extra == 'all'
25
+ Provides-Extra: auth-google
26
+ Requires-Dist: google-auth>=2.0; extra == 'auth-google'
27
+ Requires-Dist: requests>=2.20; extra == 'auth-google'
28
+ Provides-Extra: fastapi
29
+ Requires-Dist: fastapi>=0.100; extra == 'fastapi'
30
+ Requires-Dist: itsdangerous>=2.0; extra == 'fastapi'
31
+ Requires-Dist: slowapi>=0.1.9; extra == 'fastapi'
32
+ Requires-Dist: starlette; extra == 'fastapi'
33
+ Provides-Extra: gateway
34
+ Requires-Dist: fastapi>=0.100; extra == 'gateway'
35
+ Requires-Dist: httpx>=0.24; extra == 'gateway'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # app-platform
39
+
40
+ Generic web app infrastructure for PostgreSQL pooling, structured logging, auth, middleware, and gateway proxying.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install app-platform
46
+ pip install "app-platform[all]"
47
+ pip install "app-platform[fastapi]"
48
+ pip install "app-platform[auth-google]"
49
+ pip install "app-platform[gateway]"
50
+ ```
51
+
52
+ ## Included subpackages
53
+
54
+ | Subpackage | Provides |
55
+ | --- | --- |
56
+ | `db` | PostgreSQL connection pooling, pooled session helpers, migrations, and database exception utilities |
57
+ | `logging` | Structured file and JSONL logging, context helpers, logger access, and decorators for timing/error instrumentation |
58
+ | `middleware` | FastAPI middleware configuration for rate limiting, sessions, CORS, and validation/error handling |
59
+ | `auth` | Pluggable auth service base with in-memory and PostgreSQL-backed user/session stores |
60
+ | `gateway` | FastAPI router factory for proxying chat and tool approval requests to an upstream gateway |
61
+
62
+ ## Quick usage
63
+
64
+ ### Database
65
+
66
+ ```python
67
+ import os
68
+
69
+ from app_platform.db import PoolManager, get_db_session
70
+
71
+ os.environ["DATABASE_URL"] = "postgresql://postgres:postgres@localhost:5432/app"
72
+
73
+ pool_manager = PoolManager(min_connections=2, max_connections=10)
74
+ pool_manager.get_pool()
75
+
76
+ with get_db_session() as conn:
77
+ with conn.cursor() as cursor:
78
+ cursor.execute("SELECT 1 AS ok")
79
+ print(cursor.fetchone())
80
+
81
+ pool_manager.close()
82
+ ```
83
+
84
+ ### Logging
85
+
86
+ ```python
87
+ from app_platform.logging import (
88
+ LoggingManager,
89
+ configure_logging,
90
+ log_errors,
91
+ log_operation,
92
+ log_timing,
93
+ )
94
+
95
+ logging_manager: LoggingManager = configure_logging(app_name="orders", log_dir="./logs")
96
+ logger = logging_manager.get_logger("service")
97
+
98
+ @log_errors()
99
+ @log_timing(threshold_s=0.25)
100
+ @log_operation("create_order")
101
+ def create_order(order_id: str) -> None:
102
+ logger.info("creating order %s", order_id)
103
+
104
+
105
+ create_order("ord_123")
106
+ ```
107
+
108
+ ### Auth
109
+
110
+ ```python
111
+ from app_platform.auth import AuthServiceBase, InMemorySessionStore, InMemoryUserStore
112
+
113
+ users = {}
114
+ sessions = {}
115
+
116
+ auth = AuthServiceBase(
117
+ session_store=InMemorySessionStore(users_dict=users, sessions_dict=sessions),
118
+ user_store=InMemoryUserStore(users_dict=users),
119
+ )
120
+
121
+ session_id = auth.create_user_session(
122
+ {
123
+ "google_user_id": "user-123",
124
+ "email": "user@example.com",
125
+ "name": "Example User",
126
+ }
127
+ )
128
+
129
+ print(auth.get_user_by_session(session_id))
130
+ ```
131
+
132
+ ### Gateway
133
+
134
+ ```python
135
+ from fastapi import FastAPI
136
+
137
+ from app_platform.gateway import GatewayConfig, create_gateway_router
138
+
139
+ app = FastAPI()
140
+
141
+
142
+ def get_current_user():
143
+ return {"user_id": "user-123", "email": "user@example.com"}
144
+
145
+
146
+ gateway_router = create_gateway_router(
147
+ GatewayConfig(
148
+ gateway_url="https://gateway.example.com",
149
+ api_key="gateway-api-key",
150
+ channel="web",
151
+ ),
152
+ get_current_user=get_current_user,
153
+ )
154
+
155
+ app.include_router(gateway_router, prefix="/gateway")
156
+ ```
157
+
158
+ ## Requirements
159
+
160
+ Python 3.11+
161
+
162
+ ## License
163
+
164
+ MIT
@@ -0,0 +1,127 @@
1
+ # app-platform
2
+
3
+ Generic web app infrastructure for PostgreSQL pooling, structured logging, auth, middleware, and gateway proxying.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install app-platform
9
+ pip install "app-platform[all]"
10
+ pip install "app-platform[fastapi]"
11
+ pip install "app-platform[auth-google]"
12
+ pip install "app-platform[gateway]"
13
+ ```
14
+
15
+ ## Included subpackages
16
+
17
+ | Subpackage | Provides |
18
+ | --- | --- |
19
+ | `db` | PostgreSQL connection pooling, pooled session helpers, migrations, and database exception utilities |
20
+ | `logging` | Structured file and JSONL logging, context helpers, logger access, and decorators for timing/error instrumentation |
21
+ | `middleware` | FastAPI middleware configuration for rate limiting, sessions, CORS, and validation/error handling |
22
+ | `auth` | Pluggable auth service base with in-memory and PostgreSQL-backed user/session stores |
23
+ | `gateway` | FastAPI router factory for proxying chat and tool approval requests to an upstream gateway |
24
+
25
+ ## Quick usage
26
+
27
+ ### Database
28
+
29
+ ```python
30
+ import os
31
+
32
+ from app_platform.db import PoolManager, get_db_session
33
+
34
+ os.environ["DATABASE_URL"] = "postgresql://postgres:postgres@localhost:5432/app"
35
+
36
+ pool_manager = PoolManager(min_connections=2, max_connections=10)
37
+ pool_manager.get_pool()
38
+
39
+ with get_db_session() as conn:
40
+ with conn.cursor() as cursor:
41
+ cursor.execute("SELECT 1 AS ok")
42
+ print(cursor.fetchone())
43
+
44
+ pool_manager.close()
45
+ ```
46
+
47
+ ### Logging
48
+
49
+ ```python
50
+ from app_platform.logging import (
51
+ LoggingManager,
52
+ configure_logging,
53
+ log_errors,
54
+ log_operation,
55
+ log_timing,
56
+ )
57
+
58
+ logging_manager: LoggingManager = configure_logging(app_name="orders", log_dir="./logs")
59
+ logger = logging_manager.get_logger("service")
60
+
61
+ @log_errors()
62
+ @log_timing(threshold_s=0.25)
63
+ @log_operation("create_order")
64
+ def create_order(order_id: str) -> None:
65
+ logger.info("creating order %s", order_id)
66
+
67
+
68
+ create_order("ord_123")
69
+ ```
70
+
71
+ ### Auth
72
+
73
+ ```python
74
+ from app_platform.auth import AuthServiceBase, InMemorySessionStore, InMemoryUserStore
75
+
76
+ users = {}
77
+ sessions = {}
78
+
79
+ auth = AuthServiceBase(
80
+ session_store=InMemorySessionStore(users_dict=users, sessions_dict=sessions),
81
+ user_store=InMemoryUserStore(users_dict=users),
82
+ )
83
+
84
+ session_id = auth.create_user_session(
85
+ {
86
+ "google_user_id": "user-123",
87
+ "email": "user@example.com",
88
+ "name": "Example User",
89
+ }
90
+ )
91
+
92
+ print(auth.get_user_by_session(session_id))
93
+ ```
94
+
95
+ ### Gateway
96
+
97
+ ```python
98
+ from fastapi import FastAPI
99
+
100
+ from app_platform.gateway import GatewayConfig, create_gateway_router
101
+
102
+ app = FastAPI()
103
+
104
+
105
+ def get_current_user():
106
+ return {"user_id": "user-123", "email": "user@example.com"}
107
+
108
+
109
+ gateway_router = create_gateway_router(
110
+ GatewayConfig(
111
+ gateway_url="https://gateway.example.com",
112
+ api_key="gateway-api-key",
113
+ channel="web",
114
+ ),
115
+ get_current_user=get_current_user,
116
+ )
117
+
118
+ app.include_router(gateway_router, prefix="/gateway")
119
+ ```
120
+
121
+ ## Requirements
122
+
123
+ Python 3.11+
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,123 @@
1
+ """Public app_platform exports for the extracted platform package."""
2
+
3
+ from .db import (
4
+ AuthenticationError,
5
+ ConnectionError,
6
+ DataConsistencyError,
7
+ DatabaseClientBase,
8
+ DatabaseError,
9
+ DatabasePermissionError,
10
+ MigrationError,
11
+ NotFoundError,
12
+ PoolExhaustionError,
13
+ PoolManager,
14
+ SchemaError,
15
+ SessionManager,
16
+ SessionNotFoundError,
17
+ TimeoutError,
18
+ TransactionError,
19
+ ValidationError,
20
+ get_db_session,
21
+ get_pool,
22
+ handle_database_error,
23
+ is_recoverable_error,
24
+ log_database_error,
25
+ run_migration,
26
+ run_migrations_dir,
27
+ )
28
+ from .logging import (
29
+ APP_LOG_FORMAT,
30
+ DEDUP_WINDOW_S,
31
+ JSON_LOG_FORMAT,
32
+ MAX_DEDUP_KEYS,
33
+ SLOW_OPERATION_THRESHOLD,
34
+ VERY_SLOW_OPERATION_THRESHOLD,
35
+ LoggingManager,
36
+ clear_log_context,
37
+ configure_logging,
38
+ get_logger,
39
+ get_logging_manager,
40
+ log_alert,
41
+ log_error,
42
+ log_errors,
43
+ log_event,
44
+ log_operation,
45
+ log_service_status,
46
+ log_slow_operation,
47
+ log_timing,
48
+ set_log_context,
49
+ )
50
+ from .middleware import (
51
+ ApiKeyRegistry,
52
+ DEFAULT_HEADERS,
53
+ DEFAULT_METHODS,
54
+ DEFAULT_SESSION_SECRET,
55
+ MiddlewareConfig,
56
+ RateLimitConfig,
57
+ add_rate_limit_handler,
58
+ add_validation_error_handler,
59
+ configure_cors,
60
+ configure_middleware,
61
+ configure_sessions,
62
+ create_limiter,
63
+ resolve_session_secret,
64
+ )
65
+
66
+ __all__ = [
67
+ "APP_LOG_FORMAT",
68
+ "ApiKeyRegistry",
69
+ "AuthenticationError",
70
+ "ConnectionError",
71
+ "DEDUP_WINDOW_S",
72
+ "DEFAULT_HEADERS",
73
+ "DEFAULT_METHODS",
74
+ "DEFAULT_SESSION_SECRET",
75
+ "DataConsistencyError",
76
+ "DatabaseClientBase",
77
+ "DatabaseError",
78
+ "DatabasePermissionError",
79
+ "JSON_LOG_FORMAT",
80
+ "LoggingManager",
81
+ "MAX_DEDUP_KEYS",
82
+ "MigrationError",
83
+ "MiddlewareConfig",
84
+ "NotFoundError",
85
+ "PoolExhaustionError",
86
+ "PoolManager",
87
+ "RateLimitConfig",
88
+ "SLOW_OPERATION_THRESHOLD",
89
+ "SchemaError",
90
+ "SessionManager",
91
+ "SessionNotFoundError",
92
+ "TimeoutError",
93
+ "TransactionError",
94
+ "VERY_SLOW_OPERATION_THRESHOLD",
95
+ "ValidationError",
96
+ "clear_log_context",
97
+ "configure_logging",
98
+ "configure_cors",
99
+ "configure_middleware",
100
+ "configure_sessions",
101
+ "get_db_session",
102
+ "get_logger",
103
+ "get_logging_manager",
104
+ "get_pool",
105
+ "handle_database_error",
106
+ "is_recoverable_error",
107
+ "create_limiter",
108
+ "log_alert",
109
+ "log_error",
110
+ "log_database_error",
111
+ "log_errors",
112
+ "log_event",
113
+ "log_operation",
114
+ "log_service_status",
115
+ "log_slow_operation",
116
+ "log_timing",
117
+ "add_rate_limit_handler",
118
+ "add_validation_error_handler",
119
+ "resolve_session_secret",
120
+ "run_migration",
121
+ "run_migrations_dir",
122
+ "set_log_context",
123
+ ]
@@ -0,0 +1,21 @@
1
+ """Core exports for app_platform.auth."""
2
+
3
+ from .protocols import SessionStore, TokenVerifier, UserStore
4
+ from .service import AuthServiceBase
5
+ from .stores import (
6
+ InMemorySessionStore,
7
+ InMemoryUserStore,
8
+ PostgresSessionStore,
9
+ PostgresUserStore,
10
+ )
11
+
12
+ __all__ = [
13
+ "AuthServiceBase",
14
+ "InMemorySessionStore",
15
+ "InMemoryUserStore",
16
+ "PostgresSessionStore",
17
+ "PostgresUserStore",
18
+ "SessionStore",
19
+ "TokenVerifier",
20
+ "UserStore",
21
+ ]
@@ -0,0 +1,28 @@
1
+ """FastAPI dependency helpers for app_platform.auth."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Callable
6
+
7
+ from fastapi import HTTPException, Request
8
+
9
+ from .service import AuthServiceBase
10
+
11
+
12
+ def create_auth_dependency(
13
+ auth_service: AuthServiceBase,
14
+ cookie_name: str = "session_id",
15
+ ) -> Callable[[Request], dict[str, Any]]:
16
+ """Return a FastAPI dependency that resolves the current user from a cookie."""
17
+
18
+ def get_current_user(request: Request) -> dict[str, Any]:
19
+ session_id = request.cookies.get(cookie_name)
20
+ user = auth_service.get_user_by_session(session_id)
21
+ if not user:
22
+ raise HTTPException(status_code=401, detail="Authentication required")
23
+ return user
24
+
25
+ return get_current_user
26
+
27
+
28
+ __all__ = ["create_auth_dependency"]
@@ -0,0 +1,50 @@
1
+ """Google OAuth token verification for app_platform.auth."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from google.auth.transport import requests as google_requests
8
+ from google.oauth2 import id_token
9
+
10
+
11
+ class GoogleTokenVerifier:
12
+ """TokenVerifier implementation backed by google-auth."""
13
+
14
+ def __init__(
15
+ self,
16
+ client_id: Optional[str],
17
+ dev_mode: bool = False,
18
+ dev_user: Optional[Dict[str, Any]] = None,
19
+ ):
20
+ self.client_id = client_id
21
+ self.dev_mode = dev_mode
22
+ self.dev_user = dev_user or {
23
+ "user_id": "dev_user_123",
24
+ "email": "dev@example.com",
25
+ "name": "Development User",
26
+ "google_user_id": "dev_google_123",
27
+ }
28
+
29
+ def verify(self, token: str) -> tuple[Optional[Dict[str, Any]], Optional[str]]:
30
+ try:
31
+ if self.dev_mode or not self.client_id:
32
+ return dict(self.dev_user), None
33
+
34
+ id_info = id_token.verify_oauth2_token(
35
+ token,
36
+ google_requests.Request(),
37
+ self.client_id,
38
+ )
39
+ return {
40
+ "user_id": id_info["sub"],
41
+ "email": id_info["email"],
42
+ "name": id_info.get("name", ""),
43
+ "google_user_id": id_info["sub"],
44
+ }, None
45
+
46
+ except Exception as exc:
47
+ return None, f"Google token verification failed: {exc}"
48
+
49
+
50
+ __all__ = ["GoogleTokenVerifier"]
@@ -0,0 +1,50 @@
1
+ """Protocol contracts for app_platform.auth."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any, Dict, Optional, Protocol, runtime_checkable
7
+
8
+
9
+ @runtime_checkable
10
+ class SessionStore(Protocol):
11
+ """Backend contract for session persistence."""
12
+
13
+ def create_session(self, session_id: str, user_id: Any, expires_at: datetime) -> None:
14
+ """Persist a new session."""
15
+
16
+ def get_session(self, session_id: str) -> Optional[Dict[str, Any]]:
17
+ """Resolve a session id to the normalized auth payload."""
18
+
19
+ def delete_session(self, session_id: str) -> bool:
20
+ """Delete a session if present."""
21
+
22
+ def cleanup_expired(self) -> int:
23
+ """Delete expired sessions and return the cleanup count."""
24
+
25
+ def touch_session(self, session_id: str) -> None:
26
+ """Update last-accessed metadata for a session."""
27
+
28
+
29
+ @runtime_checkable
30
+ class UserStore(Protocol):
31
+ """Backend contract for user persistence."""
32
+
33
+ def get_or_create_user(
34
+ self,
35
+ provider_user_id: str,
36
+ email: str,
37
+ name: str,
38
+ ) -> tuple[Any, Dict[str, Any]]:
39
+ """Return the resolved user id and normalized user payload."""
40
+
41
+
42
+ @runtime_checkable
43
+ class TokenVerifier(Protocol):
44
+ """Contract for OAuth/OIDC token verification."""
45
+
46
+ def verify(self, token: str) -> tuple[Optional[Dict[str, Any]], Optional[str]]:
47
+ """Validate a provider token and return either user info or an error."""
48
+
49
+
50
+ __all__ = ["SessionStore", "TokenVerifier", "UserStore"]