Skip to content

Commit e0e1b33

Browse files
committed
added ability to set the auth cookie via a form (created by the new Routes__Set_Cookie)
moved multiple static values to the const__Fast_API file
1 parent 7f64c3c commit e0e1b33

11 files changed

Lines changed: 206 additions & 56 deletions

File tree

osbot_fast_api/api/Fast_API.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
from osbot_fast_api.api.Fast_API__Offline_Docs import Fast_API__Offline_Docs, FILE_PATH__STATIC__DOCS, URL__STATIC__DOCS, NAME__STATIC__DOCS
99
from osbot_fast_api.api.events.Fast_API__Http_Events import Fast_API__Http_Events
1010
from osbot_fast_api.api.routes.Routes__Config import Routes__Config
11+
from osbot_fast_api.api.routes.Routes__Set_Cookie import Routes__Set_Cookie
1112
from osbot_fast_api.schemas.Safe_Str__Fast_API__Name import Safe_Str__Fast_API__Name
1213
from osbot_fast_api.schemas.Safe_Str__Fast_API__Route__Prefix import Safe_Str__Fast_API__Route__Prefix
14+
from osbot_fast_api.schemas.consts__Fast_API import ENV_VAR__FAST_API__AUTH__API_KEY__NAME, ENV_VAR__FAST_API__AUTH__API_KEY__VALUE
1315
from osbot_fast_api.utils.Version import version__osbot_fast_api
1416

15-
DEFAULT_ROUTES_PATHS = ['/', '/config/status', '/config/version']
16-
DEFAULT__NAME__FAST_API = 'Fast_API'
17-
ENV_VAR__FAST_API__AUTH__API_KEY__NAME = 'FAST_API__AUTH__API_KEY__NAME'
18-
ENV_VAR__FAST_API__AUTH__API_KEY__VALUE = 'FAST_API__AUTH__API_KEY__VALUE'
17+
1918

2019
class Fast_API(Type_Safe):
2120
base_path : Safe_Str__Fast_API__Route__Prefix = '/'
@@ -116,8 +115,13 @@ def fast_api_utils(self):
116115
def path_static_folder(self): # override this to add support for serving static files from this directory
117116
return None
118117

119-
def mount(self, parent_app):
118+
def mount(self, parent_app): # use this from the child Fast_Api instance
120119
parent_app.mount(self.base_path, self.app())
120+
return self
121+
122+
def mount_fast_api(self, class_fast_api): # use this from the parent Fast_Api instance
123+
class_fast_api().setup().mount(self.app())
124+
return self
121125

122126
def setup(self):
123127
self.add_global_exception_handlers()
@@ -168,9 +172,10 @@ def setup_routes (self): return self # overwrite to add rules
168172
def setup_default_routes(self):
169173

170174
if self.default_routes:
171-
self.setup_add_root_route()
172-
self.setup_offline_docs ()
173-
self.add_routes(Routes__Config)
175+
self.setup_add_root_route ()
176+
self.setup_offline_docs ()
177+
self.add_routes(Routes__Config )
178+
self.add_routes(Routes__Set_Cookie)
174179

175180
def setup_add_root_route(self):
176181
from starlette.responses import RedirectResponse
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from fastapi import Request, Response
2+
from fastapi.responses import HTMLResponse
3+
from osbot_utils.type_safe.Type_Safe import Type_Safe
4+
from osbot_fast_api.api.routes.Fast_API__Routes import Fast_API__Routes
5+
from osbot_fast_api.schemas.consts__Fast_API import ENV_VAR__FAST_API__AUTH__API_KEY__NAME
6+
7+
class Schema__Set_Cookie(Type_Safe):
8+
cookie_value: str
9+
10+
class Routes__Set_Cookie(Fast_API__Routes):
11+
tag: str = 'auth'
12+
13+
def auth_cookie_form(self, request: Request): # Display form to edit auth cookie with JSON submission
14+
current_cookie = request.cookies.get(ENV_VAR__FAST_API__AUTH__API_KEY__NAME, '')
15+
16+
html_content = f"""
17+
<!DOCTYPE html>
18+
<html>
19+
<head>
20+
<title>Auth Cookie Editor</title>
21+
<style>
22+
body {{ font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }}
23+
input {{ width: 100%; padding: 10px; margin: 10px 0; }}
24+
button {{ background: #4CAF50; color: white; padding: 10px 20px; border: none; cursor: pointer; }}
25+
button:hover {{ background: #45a049; }}
26+
button:disabled {{ background: #ccc; cursor: not-allowed; }}
27+
.current {{ background: #f0f0f0; padding: 10px; margin: 10px 0; }}
28+
.message {{ padding: 10px; margin: 10px 0; border-radius: 4px; }}
29+
.success {{ background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }}
30+
.error {{ background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }}
31+
</style>
32+
</head>
33+
<body>
34+
<h1>Auth Cookie Editor</h1>
35+
<div class="current">
36+
<strong>Current Cookie Value:</strong><br>
37+
<code id="currentValue">{current_cookie or '(not set)'}</code>
38+
</div>
39+
<div id="message"></div>
40+
<form id="cookieForm">
41+
<label for="cookie_value">New Cookie Value:</label>
42+
<input type="text" id="cookie_value" name="cookie_value" value="{current_cookie}">
43+
<button type="submit" id="submitBtn">Set Cookie</button>
44+
</form>
45+
46+
<script>
47+
const form = document.getElementById('cookieForm');
48+
const messageDiv = document.getElementById('message');
49+
const submitBtn = document.getElementById('submitBtn');
50+
const currentValueSpan = document.getElementById('currentValue');
51+
const cookieInput = document.getElementById('cookie_value');
52+
53+
const setAuthCookie = async (value) => {{
54+
const res = await fetch("/auth/set-auth-cookie", {{
55+
method: "POST",
56+
headers: {{ "Content-Type": "application/json" }},
57+
body: JSON.stringify({{ cookie_value: value }}),
58+
credentials: "same-origin"
59+
}});
60+
61+
if (!res.ok) {{
62+
const errorText = await res.text();
63+
throw new Error(errorText || `HTTP error! status: ${{res.status}}`);
64+
}}
65+
66+
return res.json();
67+
}};
68+
69+
form.addEventListener('submit', async (e) => {{
70+
e.preventDefault();
71+
72+
const value = cookieInput.value;
73+
74+
// Disable button during submission
75+
submitBtn.disabled = true;
76+
submitBtn.textContent = 'Setting...';
77+
78+
try {{
79+
const result = await setAuthCookie(value);
80+
81+
// Show success message
82+
messageDiv.className = 'message success';
83+
messageDiv.textContent = result.message || 'Cookie set successfully';
84+
85+
// Update current value display
86+
currentValueSpan.textContent = value || '(not set)';
87+
88+
// Clear message after 3 seconds
89+
setTimeout(() => {{
90+
messageDiv.className = '';
91+
messageDiv.textContent = '';
92+
}}, 3000);
93+
94+
}} catch (error) {{
95+
// Show error message
96+
messageDiv.className = 'message error';
97+
messageDiv.textContent = `Error: ${{error.message}}`;
98+
}} finally {{
99+
// Re-enable button
100+
submitBtn.disabled = false;
101+
submitBtn.textContent = 'Set Cookie';
102+
}}
103+
}});
104+
</script>
105+
</body>
106+
</html>
107+
"""
108+
109+
return HTMLResponse(content=html_content)
110+
111+
def set_auth_cookie(self, set_cookie: Schema__Set_Cookie, response: Response):
112+
"""Set the auth cookie via JSON request"""
113+
cookie_name = ENV_VAR__FAST_API__AUTH__API_KEY__NAME
114+
response.set_cookie(
115+
key=cookie_name,
116+
value=set_cookie.cookie_value,
117+
httponly=True,
118+
secure=True,
119+
samesite='strict'
120+
)
121+
return {
122+
"message": "Cookie set successfully",
123+
"cookie_name": cookie_name,
124+
"cookie_value": set_cookie.cookie_value
125+
}
126+
127+
def setup_routes(self):
128+
self.add_route_get(self.auth_cookie_form)
129+
self.add_route_post(self.set_auth_cookie)

osbot_fast_api/examples/ex_1_simple/Fast_API__Simple.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
from osbot_utils.utils.Files import path_combine
2-
3-
from osbot_fast_api.api.Fast_API import Fast_API
4-
from osbot_fast_api.examples import ex_1_simple
5-
from osbot_fast_api.utils.Fast_API_Utils import ROUTE_REDIRECT_TO_DOCS
1+
from osbot_utils.utils.Files import path_combine
2+
from osbot_fast_api.api.Fast_API import Fast_API
3+
from osbot_fast_api.examples import ex_1_simple
64

75
EX_1__FOLDER_NAME__STATIC_FOLDER = 'static_files'
8-
EX_1_ROUTES = [{'http_methods': ['GET', 'HEAD'], 'http_path': '/static' , 'method_name': 'static' },
9-
{'http_methods': ['POST' ], 'http_path': '/an-post', 'method_name': 'an_post' }]
6+
EX_1_ROUTES = [{'http_methods': ['GET', 'HEAD'], 'http_path': '/static' , 'method_name': 'static' },
7+
{'http_methods': ['POST' ], 'http_path': '/an-post', 'method_name': 'an_post' }]
108

119
class Fast_API__Simple(Fast_API):
1210

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
11
import re
22

3-
REGEX__SAFE__STR__FAST_API__TITLE = re.compile(r'[^a-zA-Z0-9 _()-]')
4-
5-
6-
EXPECTED_ROUTES_METHODS = ['info', 'redirect_to_docs', 'routes__html', 'routes__json', 'status', 'version' ]
7-
EXPECTED_ROUTES_PATHS = ['/' ,
8-
'/config/info' ,
9-
'/config/routes/html',
10-
'/config/routes/json',
11-
'/config/status' ,
12-
'/config/version' ]
13-
EXPECTED_DEFAULT_ROUTES = ['/docs', '/openapi.json', '/redoc', '/static-docs' ]
14-
15-
16-
ROUTES__CONFIG = [{ 'http_methods': ['GET' ], 'http_path': '/config/info' , 'method_name': 'info' },
17-
{ 'http_methods': ['GET' ], 'http_path': '/config/status' , 'method_name': 'status' },
18-
{ 'http_methods': ['GET' ], 'http_path': '/config/version' , 'method_name': 'version' },
19-
{ 'http_methods': ['GET' ], 'http_path': '/config/routes/json', 'method_name': 'routes__json'},
20-
{ 'http_methods': ['GET' ], 'http_path': '/config/routes/html', 'method_name': 'routes__html'}]
21-
ROUTES__STATIC_DOCS = [{'http_methods': ['GET', 'HEAD'], 'http_path': '/static-docs' , 'method_name': 'static-docs' }]
22-
ROUTES_PATHS__CONFIG = ['/config/status', '/config/version']
3+
# todo: the names of these variables need a bit of refactoring and normalising
4+
5+
REGEX__SAFE__STR__FAST_API__TITLE = re.compile(r'[^a-zA-Z0-9 _()-]')
6+
7+
DEFAULT_ROUTES_PATHS = ['/', '/config/status', '/config/version']
8+
DEFAULT__NAME__FAST_API = 'Fast_API'
9+
ENV_VAR__FAST_API__AUTH__API_KEY__NAME = 'FAST_API__AUTH__API_KEY__NAME'
10+
ENV_VAR__FAST_API__AUTH__API_KEY__VALUE = 'FAST_API__AUTH__API_KEY__VALUE'
11+
12+
ROUTE_REDIRECT_TO_DOCS = {'http_methods': ['GET' ], 'http_path': '/' , 'method_name': 'redirect_to_docs'}
13+
FAST_API_DEFAULT_ROUTES_PATHS = ['/docs', '/docs/oauth2-redirect', '/openapi.json', '/redoc', '/static-docs']
14+
FAST_API_DEFAULT_ROUTES = [ { 'http_methods': ['GET','HEAD'], 'http_path': '/openapi.json' , 'method_name': 'openapi' },
15+
ROUTE_REDIRECT_TO_DOCS ,
16+
{ 'http_methods': ['GET' ], 'http_path': '/docs' , 'method_name': 'swagger_ui_html' },
17+
{ 'http_methods': ['GET' ], 'http_path': '/redoc' , 'method_name': 'redoc_html' }]
18+
19+
20+
EXPECTED_ROUTES_METHODS = ['auth_cookie_form',
21+
'info' ,
22+
'redirect_to_docs',
23+
'routes__html' ,
24+
'routes__json' ,
25+
'set_auth_cookie' ,
26+
'status' ,
27+
'version' ]
28+
EXPECTED_ROUTES_PATHS = ['/' ,
29+
'/auth/auth-cookie-form',
30+
'/auth/set-auth-cookie' ,
31+
'/config/info' ,
32+
'/config/routes/html' ,
33+
'/config/routes/json' ,
34+
'/config/status' ,
35+
'/config/version' ]
36+
EXPECTED_DEFAULT_ROUTES = ['/docs', '/openapi.json', '/redoc', '/static-docs' ]
37+
38+
39+
ROUTES__CONFIG = [{ 'http_methods': ['GET' ], 'http_path': '/config/info' , 'method_name': 'info' },
40+
{ 'http_methods': ['GET' ], 'http_path': '/config/status' , 'method_name': 'status' },
41+
{ 'http_methods': ['GET' ], 'http_path': '/config/version' , 'method_name': 'version' },
42+
{ 'http_methods': ['GET' ], 'http_path': '/config/routes/json' , 'method_name': 'routes__json' },
43+
{ 'http_methods': ['GET' ], 'http_path': '/config/routes/html' , 'method_name': 'routes__html' },
44+
{ 'http_methods': ['GET' ], 'http_path': '/auth/auth-cookie-form', 'method_name': 'auth_cookie_form' },
45+
{ 'http_methods': ['POST' ], 'http_path': '/auth/set-auth-cookie' , 'method_name': 'set_auth_cookie' },]
46+
ROUTES__STATIC_DOCS = [{'http_methods': ['GET', 'HEAD'], 'http_path': '/static-docs' , 'method_name': 'static-docs' }]
47+
ROUTES_PATHS__CONFIG = ['/config/status', '/config/version']

osbot_fast_api/utils/Fast_API_Utils.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
from fastapi.routing import APIWebSocketRoute
2-
from starlette.middleware.wsgi import WSGIMiddleware
3-
from starlette.routing import Mount
4-
from starlette.staticfiles import StaticFiles
1+
from fastapi.routing import APIWebSocketRoute
2+
from starlette.middleware.wsgi import WSGIMiddleware
3+
from starlette.routing import Mount
4+
from starlette.staticfiles import StaticFiles
5+
from osbot_fast_api.schemas.consts__Fast_API import FAST_API_DEFAULT_ROUTES_PATHS
56

6-
ROUTE_REDIRECT_TO_DOCS = {'http_methods': ['GET' ], 'http_path': '/' , 'method_name': 'redirect_to_docs'}
7-
FAST_API_DEFAULT_ROUTES_PATHS = ['/docs', '/docs/oauth2-redirect', '/openapi.json', '/redoc', '/static-docs']
8-
FAST_API_DEFAULT_ROUTES = [ { 'http_methods': ['GET','HEAD'], 'http_path': '/openapi.json' , 'method_name': 'openapi' },
9-
ROUTE_REDIRECT_TO_DOCS ,
10-
{ 'http_methods': ['GET' ], 'http_path': '/docs' , 'method_name': 'swagger_ui_html' },
11-
{ 'http_methods': ['GET' ], 'http_path': '/redoc' , 'method_name': 'redoc_html' }]
127

138
class Fast_API_Utils:
149

tests/unit/api/decorators/test_route_path.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from unittest import TestCase
22
from osbot_fast_api.api.Fast_API import Fast_API
3-
from osbot_fast_api.api.routes.Fast_API__Routes import Fast_API__Routes
3+
from osbot_fast_api.api.routes.Fast_API__Routes import Fast_API__Routes
44
from osbot_fast_api.api.decorators.route_path import route_path
55
from osbot_utils.type_safe.Type_Safe import Type_Safe
66
from osbot_utils.type_safe.primitives.safe_str.Safe_Str import Safe_Str

tests/unit/api/routes/http_shell/test_Http_Shell__Client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from unittest import TestCase
55
from osbot_utils.utils.Env import load_dotenv
66
from osbot_utils.utils.Misc import list_set
7+
from osbot_fast_api.schemas.consts__Fast_API import EXPECTED_ROUTES_PATHS
78
from osbot_fast_api.utils.Fast_API_Server import Fast_API_Server
89
from osbot_fast_api.api.Fast_API import Fast_API
910
from osbot_fast_api.utils.http_shell.Http_Shell__Client import Http_Shell__Client
@@ -56,7 +57,7 @@ def test__fast_api_server(self):
5657
del options_headers['date']
5758

5859
assert response_shell_invoke.json() == expected_result
59-
assert self.fast_api.routes_paths() == ['/', '/config/info', '/config/routes/html', '/config/routes/json', '/config/status', '/config/version', '/http-shell-server']
60+
assert self.fast_api.routes_paths() == EXPECTED_ROUTES_PATHS + [ '/http-shell-server']
6061
assert self.fast_api_server.port > 19999
6162
assert self.fast_api_server.is_port_open() is True
6263
assert response_options.json() == { "detail" : "Method Not Allowed" }

tests/unit/api/test_Fast_API.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
from starlette.testclient import TestClient
99
from osbot_fast_api.api.Fast_API import Fast_API
1010
from osbot_fast_api.schemas.Safe_Str__Fast_API__Name import Safe_Str__Fast_API__Name
11-
from osbot_fast_api.schemas.consts__Fast_API import EXPECTED_ROUTES_PATHS, EXPECTED_ROUTES_METHODS, EXPECTED_DEFAULT_ROUTES, ROUTES__CONFIG, ROUTES__STATIC_DOCS
12-
from osbot_fast_api.utils.Fast_API_Utils import FAST_API_DEFAULT_ROUTES
11+
from osbot_fast_api.schemas.consts__Fast_API import EXPECTED_ROUTES_PATHS, EXPECTED_ROUTES_METHODS, EXPECTED_DEFAULT_ROUTES, ROUTES__CONFIG, ROUTES__STATIC_DOCS, FAST_API_DEFAULT_ROUTES
1312
from osbot_fast_api.utils.Fast_API_Utils import Fast_API_Utils
1413
from osbot_fast_api.utils.Version import version__osbot_fast_api
1514
from tests.unit.fast_api__for_tests import fast_api, fast_api_client

tests/unit/cli/test_Fast_API__CLI.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import requests
2-
from unittest import TestCase
3-
from unittest.mock import patch
4-
from osbot_fast_api.cli.Fast_API__CLI import Fast_API__CLI
2+
from unittest import TestCase
3+
from unittest.mock import patch
4+
from osbot_fast_api.cli.Fast_API__CLI import Fast_API__CLI
55
from osbot_fast_api.schemas.consts__Fast_API import EXPECTED_ROUTES_PATHS
66

77

0 commit comments

Comments
 (0)