feather-framework 0.8.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.
- feather/__init__.py +284 -0
- feather/auth/__init__.py +131 -0
- feather/auth/decorators.py +518 -0
- feather/auth/domains.py +171 -0
- feather/auth/google.py +583 -0
- feather/auth/permissions.py +61 -0
- feather/auth/permissions_logic.py +66 -0
- feather/auth/roles.py +80 -0
- feather/auth/routes.py +74 -0
- feather/auth/setup.py +192 -0
- feather/auth/tenancy.py +160 -0
- feather/cache/__init__.py +196 -0
- feather/cache/base.py +196 -0
- feather/cache/decorators.py +352 -0
- feather/cache/memory.py +211 -0
- feather/cache/redis.py +288 -0
- feather/cli/__init__.py +123 -0
- feather/cli/build.py +133 -0
- feather/cli/db.py +128 -0
- feather/cli/dev.py +97 -0
- feather/cli/dx.py +457 -0
- feather/cli/generate.py +602 -0
- feather/cli/jobs.py +238 -0
- feather/cli/new.py +5934 -0
- feather/cli/platform_admin.py +83 -0
- feather/core/__init__.py +6 -0
- feather/core/app.py +433 -0
- feather/core/config.py +153 -0
- feather/core/decorators.py +420 -0
- feather/core/discovery.py +140 -0
- feather/core/error_handlers.py +356 -0
- feather/core/health.py +116 -0
- feather/core/helpers.py +318 -0
- feather/core/middleware.py +253 -0
- feather/db/__init__.py +146 -0
- feather/db/base.py +272 -0
- feather/db/mixins.py +600 -0
- feather/db/pagination.py +275 -0
- feather/events/__init__.py +67 -0
- feather/events/dispatcher.py +269 -0
- feather/events/events.py +160 -0
- feather/exceptions/__init__.py +350 -0
- feather/jobs/__init__.py +399 -0
- feather/jobs/base.py +248 -0
- feather/jobs/monitoring.py +218 -0
- feather/jobs/rq.py +326 -0
- feather/jobs/scheduler.py +287 -0
- feather/jobs/sync.py +169 -0
- feather/jobs/thread.py +585 -0
- feather/serializers/__init__.py +106 -0
- feather/serializers/base.py +477 -0
- feather/services/__init__.py +38 -0
- feather/services/base.py +581 -0
- feather/static/api.js +330 -0
- feather/static/apple-touch-icon.png +0 -0
- feather/static/dropdown.js +244 -0
- feather/static/favicon.svg +12 -0
- feather/static/feather.js +546 -0
- feather/static/icon-192.png +0 -0
- feather/static/icon-512.png +0 -0
- feather/storage/__init__.py +94 -0
- feather/storage/base.py +176 -0
- feather/storage/gcs.py +250 -0
- feather/storage/local.py +193 -0
- feather/templates/components/__init__.html +20 -0
- feather/templates/components/alert.html +13 -0
- feather/templates/components/button.html +27 -0
- feather/templates/components/card.html +15 -0
- feather/templates/components/confirm_modal.html +44 -0
- feather/templates/components/dropdown.html +112 -0
- feather/templates/components/icon.html +17 -0
- feather/templates/components/input.html +28 -0
- feather/templates/components/modal.html +18 -0
- feather/templates/components/prompt_modal.html +120 -0
- feather/templates/components/toast.html +101 -0
- feather/templates/errors/auth_required.html +94 -0
- feather/templates/errors/error.html +30 -0
- feather_framework-0.8.0.dist-info/METADATA +2007 -0
- feather_framework-0.8.0.dist-info/RECORD +83 -0
- feather_framework-0.8.0.dist-info/WHEEL +5 -0
- feather_framework-0.8.0.dist-info/entry_points.txt +2 -0
- feather_framework-0.8.0.dist-info/licenses/LICENSE +21 -0
- feather_framework-0.8.0.dist-info/top_level.txt +1 -0
feather/__init__.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Feather - A Flask-based web framework for solo developers using AI coding tools.
|
|
3
|
+
|
|
4
|
+
Server-first architecture with progressive enhancement, batteries included.
|
|
5
|
+
|
|
6
|
+
Quick Start
|
|
7
|
+
-----------
|
|
8
|
+
Create a new project and start developing in seconds::
|
|
9
|
+
|
|
10
|
+
$ pip install feather-framework
|
|
11
|
+
$ feather new myapp
|
|
12
|
+
$ cd myapp
|
|
13
|
+
$ feather dev
|
|
14
|
+
|
|
15
|
+
Then open http://localhost:5000 in your browser.
|
|
16
|
+
|
|
17
|
+
Basic Usage
|
|
18
|
+
-----------
|
|
19
|
+
Your app.py is just 3 lines::
|
|
20
|
+
|
|
21
|
+
from feather import Feather
|
|
22
|
+
|
|
23
|
+
app = Feather(__name__)
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
app.run()
|
|
27
|
+
|
|
28
|
+
Feather auto-discovers your models, services, and routes. No configuration needed.
|
|
29
|
+
|
|
30
|
+
Creating Routes
|
|
31
|
+
---------------
|
|
32
|
+
API routes (mounted at /api/*)::
|
|
33
|
+
|
|
34
|
+
from feather import api
|
|
35
|
+
|
|
36
|
+
@api.get('/users')
|
|
37
|
+
def list_users():
|
|
38
|
+
return {'users': [...]}
|
|
39
|
+
|
|
40
|
+
Page routes (mounted at /*)::
|
|
41
|
+
|
|
42
|
+
from feather import page
|
|
43
|
+
from flask import render_template
|
|
44
|
+
|
|
45
|
+
@page.get('/')
|
|
46
|
+
def home():
|
|
47
|
+
return render_template('pages/home.html')
|
|
48
|
+
|
|
49
|
+
Using Services
|
|
50
|
+
--------------
|
|
51
|
+
Services contain your business logic::
|
|
52
|
+
|
|
53
|
+
from feather import Service
|
|
54
|
+
from feather.exceptions import NotFoundError
|
|
55
|
+
|
|
56
|
+
class UserService(Service):
|
|
57
|
+
def get_by_id(self, user_id: str):
|
|
58
|
+
return self.get_or_404(User, user_id)
|
|
59
|
+
|
|
60
|
+
def create(self, email: str, username: str):
|
|
61
|
+
user = User(email=email, username=username)
|
|
62
|
+
self.save(user)
|
|
63
|
+
return user
|
|
64
|
+
|
|
65
|
+
Inject services into routes::
|
|
66
|
+
|
|
67
|
+
from feather import api, inject
|
|
68
|
+
|
|
69
|
+
@api.get('/users/<user_id>')
|
|
70
|
+
@inject(UserService)
|
|
71
|
+
def get_user(user_id, user_service):
|
|
72
|
+
user = user_service.get_by_id(user_id)
|
|
73
|
+
return {'user': user.to_dict()}
|
|
74
|
+
|
|
75
|
+
Error Handling
|
|
76
|
+
--------------
|
|
77
|
+
Use exceptions for clean error handling::
|
|
78
|
+
|
|
79
|
+
from feather.exceptions import ValidationError, NotFoundError
|
|
80
|
+
|
|
81
|
+
if not email:
|
|
82
|
+
raise ValidationError('Email is required', field='email')
|
|
83
|
+
|
|
84
|
+
user = User.query.get(user_id)
|
|
85
|
+
if not user:
|
|
86
|
+
raise NotFoundError('User', user_id)
|
|
87
|
+
|
|
88
|
+
Events
|
|
89
|
+
------
|
|
90
|
+
Dispatch events for loose coupling::
|
|
91
|
+
|
|
92
|
+
from feather import dispatch, listen
|
|
93
|
+
from feather.events import Event
|
|
94
|
+
|
|
95
|
+
class UserCreatedEvent(Event):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@listen(UserCreatedEvent)
|
|
99
|
+
def send_welcome_email(event):
|
|
100
|
+
# Send email to event.user_id
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
# In your service:
|
|
104
|
+
dispatch(UserCreatedEvent(user_id=user.id))
|
|
105
|
+
|
|
106
|
+
For more information, see the full documentation in feather_framework.md.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
__version__ = "0.8.0"
|
|
110
|
+
|
|
111
|
+
# =============================================================================
|
|
112
|
+
# Core Application
|
|
113
|
+
# =============================================================================
|
|
114
|
+
# The main Feather class extends Flask with auto-discovery and batteries-included
|
|
115
|
+
# features. Use this as your application entry point.
|
|
116
|
+
from feather.core.app import Feather
|
|
117
|
+
|
|
118
|
+
# Route blueprints and decorators for building your application:
|
|
119
|
+
# - api: Blueprint for API routes, mounted at /api/*
|
|
120
|
+
# - page: Blueprint for page routes, mounted at /*
|
|
121
|
+
# - inject: Decorator to inject service instances into routes
|
|
122
|
+
# - auth_required: Decorator to require authentication
|
|
123
|
+
# - csrf_exempt: Decorator to exempt webhook routes from CSRF protection
|
|
124
|
+
from feather.core.decorators import api, page, inject, auth_required, csrf_exempt
|
|
125
|
+
|
|
126
|
+
# =============================================================================
|
|
127
|
+
# Services Layer
|
|
128
|
+
# =============================================================================
|
|
129
|
+
# Services contain business logic and are the recommended way to interact with
|
|
130
|
+
# your data. Keep routes thin - delegate to services.
|
|
131
|
+
from feather.services.base import Service, transactional, singleton
|
|
132
|
+
|
|
133
|
+
# =============================================================================
|
|
134
|
+
# Authentication Decorators
|
|
135
|
+
# =============================================================================
|
|
136
|
+
# Role-based access control decorators for routes.
|
|
137
|
+
from feather.auth.decorators import (
|
|
138
|
+
admin_required,
|
|
139
|
+
role_required,
|
|
140
|
+
permission_required,
|
|
141
|
+
platform_admin_required,
|
|
142
|
+
rate_limit,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# =============================================================================
|
|
146
|
+
# Tenancy
|
|
147
|
+
# =============================================================================
|
|
148
|
+
# Multi-tenant utilities for tenant isolation.
|
|
149
|
+
from feather.auth.tenancy import get_current_tenant_id, tenant_required
|
|
150
|
+
|
|
151
|
+
# =============================================================================
|
|
152
|
+
# Database
|
|
153
|
+
# =============================================================================
|
|
154
|
+
# SQLAlchemy database instance and base model class. All models should inherit
|
|
155
|
+
# from Model to get common functionality like save(), delete(), get_by_id().
|
|
156
|
+
from feather.db.base import db, Model
|
|
157
|
+
|
|
158
|
+
# Database mixins for common patterns - use only what you need
|
|
159
|
+
from feather.db.mixins import (
|
|
160
|
+
UUIDMixin,
|
|
161
|
+
TimestampMixin,
|
|
162
|
+
SoftDeleteMixin,
|
|
163
|
+
OrderingMixin,
|
|
164
|
+
TenantScopedMixin,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Pagination utilities for list endpoints
|
|
168
|
+
from feather.db.pagination import paginate, PaginatedResult
|
|
169
|
+
|
|
170
|
+
# =============================================================================
|
|
171
|
+
# Event System
|
|
172
|
+
# =============================================================================
|
|
173
|
+
# Pub/sub pattern for loose coupling between components. Dispatch events after
|
|
174
|
+
# actions complete, and listen for them in separate handlers.
|
|
175
|
+
from feather.events.dispatcher import dispatch, listen
|
|
176
|
+
|
|
177
|
+
# =============================================================================
|
|
178
|
+
# Storage
|
|
179
|
+
# =============================================================================
|
|
180
|
+
# File storage abstraction supporting local filesystem and Google Cloud Storage.
|
|
181
|
+
# Use get_storage() to get the configured backend.
|
|
182
|
+
from feather.storage import get_storage
|
|
183
|
+
|
|
184
|
+
# =============================================================================
|
|
185
|
+
# Caching
|
|
186
|
+
# =============================================================================
|
|
187
|
+
# Response and function result caching with memory or Redis backends.
|
|
188
|
+
from feather.cache import get_cache, cached, cache_response
|
|
189
|
+
|
|
190
|
+
# =============================================================================
|
|
191
|
+
# Background Jobs
|
|
192
|
+
# =============================================================================
|
|
193
|
+
# Background job processing with sync (dev) or RQ (production) backends.
|
|
194
|
+
from feather.jobs import get_queue, job, scheduled
|
|
195
|
+
|
|
196
|
+
# =============================================================================
|
|
197
|
+
# Request Tracking
|
|
198
|
+
# =============================================================================
|
|
199
|
+
# Get the current request ID for logging and tracing.
|
|
200
|
+
from feather.core.middleware import get_request_id
|
|
201
|
+
|
|
202
|
+
# =============================================================================
|
|
203
|
+
# Exceptions
|
|
204
|
+
# =============================================================================
|
|
205
|
+
# Use these exceptions throughout your application for consistent error handling.
|
|
206
|
+
# They are automatically converted to proper JSON API responses with appropriate
|
|
207
|
+
# HTTP status codes.
|
|
208
|
+
from feather.exceptions import (
|
|
209
|
+
FeatherException, # Base exception - 500 Internal Server Error
|
|
210
|
+
ValidationError, # Invalid input - 400 Bad Request
|
|
211
|
+
AuthenticationError, # Not logged in - 401 Unauthorized
|
|
212
|
+
AuthorizationError, # No permission - 403 Forbidden
|
|
213
|
+
NotFoundError, # Resource not found - 404 Not Found
|
|
214
|
+
ConflictError, # Already exists - 409 Conflict
|
|
215
|
+
StorageError, # File storage failed - 500 Internal Server Error
|
|
216
|
+
DatabaseError, # Database operation failed - 500 Internal Server Error
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# =============================================================================
|
|
220
|
+
# Public API
|
|
221
|
+
# =============================================================================
|
|
222
|
+
# These are the recommended imports for Feather applications. Import what you
|
|
223
|
+
# need directly from feather:
|
|
224
|
+
#
|
|
225
|
+
# from feather import Feather, api, Service, ValidationError
|
|
226
|
+
#
|
|
227
|
+
__all__ = [
|
|
228
|
+
# Core - The application class and route decorators
|
|
229
|
+
"Feather",
|
|
230
|
+
"api",
|
|
231
|
+
"page",
|
|
232
|
+
"inject",
|
|
233
|
+
"auth_required",
|
|
234
|
+
"csrf_exempt",
|
|
235
|
+
# Services - Business logic layer
|
|
236
|
+
"Service",
|
|
237
|
+
"transactional",
|
|
238
|
+
"singleton",
|
|
239
|
+
# Authentication - Role-based access control
|
|
240
|
+
"admin_required",
|
|
241
|
+
"role_required",
|
|
242
|
+
"permission_required",
|
|
243
|
+
"platform_admin_required",
|
|
244
|
+
"rate_limit",
|
|
245
|
+
# Tenancy - Multi-tenant utilities
|
|
246
|
+
"get_current_tenant_id",
|
|
247
|
+
"tenant_required",
|
|
248
|
+
# Database - SQLAlchemy integration
|
|
249
|
+
"db",
|
|
250
|
+
"Model",
|
|
251
|
+
# Database Mixins - Common model patterns
|
|
252
|
+
"UUIDMixin",
|
|
253
|
+
"TimestampMixin",
|
|
254
|
+
"SoftDeleteMixin",
|
|
255
|
+
"OrderingMixin",
|
|
256
|
+
"TenantScopedMixin",
|
|
257
|
+
# Pagination - List endpoints
|
|
258
|
+
"paginate",
|
|
259
|
+
"PaginatedResult",
|
|
260
|
+
# Events - Pub/sub for loose coupling
|
|
261
|
+
"dispatch",
|
|
262
|
+
"listen",
|
|
263
|
+
# Storage - File upload/download
|
|
264
|
+
"get_storage",
|
|
265
|
+
# Caching - Response and result caching
|
|
266
|
+
"get_cache",
|
|
267
|
+
"cached",
|
|
268
|
+
"cache_response",
|
|
269
|
+
# Background Jobs - Async job processing
|
|
270
|
+
"get_queue",
|
|
271
|
+
"job",
|
|
272
|
+
"scheduled",
|
|
273
|
+
# Request Tracking - For logging and tracing
|
|
274
|
+
"get_request_id",
|
|
275
|
+
# Exceptions - Error handling (automatically converted to JSON responses)
|
|
276
|
+
"FeatherException",
|
|
277
|
+
"ValidationError",
|
|
278
|
+
"AuthenticationError",
|
|
279
|
+
"AuthorizationError",
|
|
280
|
+
"NotFoundError",
|
|
281
|
+
"ConflictError",
|
|
282
|
+
"StorageError",
|
|
283
|
+
"DatabaseError",
|
|
284
|
+
]
|
feather/auth/__init__.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication Module
|
|
3
|
+
=====================
|
|
4
|
+
|
|
5
|
+
Provides authentication and authorization for Feather applications.
|
|
6
|
+
|
|
7
|
+
Feather uses a **two-axis authority model**:
|
|
8
|
+
1. **Tenant role** (`role`): Authority within a tenant (admin, editor, user)
|
|
9
|
+
2. **Platform authority** (`is_platform_admin`): Cross-tenant operator power
|
|
10
|
+
|
|
11
|
+
This module includes:
|
|
12
|
+
- Flask-Login integration for session management
|
|
13
|
+
- Google OAuth 2.0 support
|
|
14
|
+
- Role-based and permission-based access control
|
|
15
|
+
- Multi-tenant isolation utilities
|
|
16
|
+
- Domain validation for tenant assignment
|
|
17
|
+
|
|
18
|
+
Quick Start
|
|
19
|
+
-----------
|
|
20
|
+
Enable authentication in your app::
|
|
21
|
+
|
|
22
|
+
# app.py
|
|
23
|
+
from feather import Feather
|
|
24
|
+
|
|
25
|
+
app = Feather(__name__)
|
|
26
|
+
# Auth is automatically initialized if User model exists
|
|
27
|
+
# and GOOGLE_CLIENT_ID is configured
|
|
28
|
+
|
|
29
|
+
Using Decorators
|
|
30
|
+
----------------
|
|
31
|
+
Protect routes with role or permission requirements::
|
|
32
|
+
|
|
33
|
+
from feather import api, auth_required
|
|
34
|
+
from feather.auth import admin_required, role_required, permission_required
|
|
35
|
+
|
|
36
|
+
@api.get('/admin/users')
|
|
37
|
+
@admin_required
|
|
38
|
+
def list_users():
|
|
39
|
+
return {'users': [...]}
|
|
40
|
+
|
|
41
|
+
@api.post('/resources')
|
|
42
|
+
@permission_required('resources.create')
|
|
43
|
+
def create_resource():
|
|
44
|
+
return {'resource': {...}}
|
|
45
|
+
|
|
46
|
+
@api.post('/platform/tenants')
|
|
47
|
+
@platform_admin_required
|
|
48
|
+
def create_tenant():
|
|
49
|
+
# Only platform admins can manage tenants
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
Tenant Isolation
|
|
53
|
+
----------------
|
|
54
|
+
Use tenant utilities in your services::
|
|
55
|
+
|
|
56
|
+
from feather.auth import get_current_tenant_id
|
|
57
|
+
from models import Resource
|
|
58
|
+
|
|
59
|
+
def list_resources():
|
|
60
|
+
tenant_id = get_current_tenant_id()
|
|
61
|
+
return Resource.for_tenant(tenant_id).all()
|
|
62
|
+
|
|
63
|
+
Google OAuth
|
|
64
|
+
------------
|
|
65
|
+
Configure in .env::
|
|
66
|
+
|
|
67
|
+
GOOGLE_CLIENT_ID=your-client-id
|
|
68
|
+
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
69
|
+
|
|
70
|
+
Then users can log in at /auth/google/login
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
from feather.auth.setup import init_auth, login_manager, set_user_loader
|
|
74
|
+
|
|
75
|
+
# Decorators
|
|
76
|
+
from feather.auth.decorators import (
|
|
77
|
+
admin_required,
|
|
78
|
+
auth_required,
|
|
79
|
+
permission_required,
|
|
80
|
+
platform_admin_required,
|
|
81
|
+
rate_limit,
|
|
82
|
+
role_required,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Tenancy
|
|
86
|
+
from feather.auth.tenancy import (
|
|
87
|
+
get_current_tenant_id,
|
|
88
|
+
require_same_tenant,
|
|
89
|
+
tenant_required,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Roles and permissions
|
|
93
|
+
from feather.auth.roles import effective_roles, ROLE_INHERITS
|
|
94
|
+
from feather.auth.permissions import ROLE_PERMISSIONS
|
|
95
|
+
from feather.auth.permissions_logic import effective_permissions
|
|
96
|
+
|
|
97
|
+
# Domain validation
|
|
98
|
+
from feather.auth.domains import (
|
|
99
|
+
extract_domain,
|
|
100
|
+
get_tenant_slug_from_domain,
|
|
101
|
+
is_public_email_domain,
|
|
102
|
+
PUBLIC_EMAIL_DOMAINS,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
__all__ = [
|
|
106
|
+
# Setup
|
|
107
|
+
"init_auth",
|
|
108
|
+
"login_manager",
|
|
109
|
+
"set_user_loader",
|
|
110
|
+
# Decorators
|
|
111
|
+
"admin_required",
|
|
112
|
+
"auth_required",
|
|
113
|
+
"permission_required",
|
|
114
|
+
"platform_admin_required",
|
|
115
|
+
"rate_limit",
|
|
116
|
+
"role_required",
|
|
117
|
+
# Tenancy
|
|
118
|
+
"get_current_tenant_id",
|
|
119
|
+
"require_same_tenant",
|
|
120
|
+
"tenant_required",
|
|
121
|
+
# Roles and permissions
|
|
122
|
+
"effective_roles",
|
|
123
|
+
"effective_permissions",
|
|
124
|
+
"ROLE_INHERITS",
|
|
125
|
+
"ROLE_PERMISSIONS",
|
|
126
|
+
# Domain validation
|
|
127
|
+
"extract_domain",
|
|
128
|
+
"get_tenant_slug_from_domain",
|
|
129
|
+
"is_public_email_domain",
|
|
130
|
+
"PUBLIC_EMAIL_DOMAINS",
|
|
131
|
+
]
|