sweatstack 0.56.0__py3-none-any.whl → 0.57.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.
sweatstack/streamlit.py CHANGED
@@ -114,6 +114,9 @@ class StreamlitAuth:
114
114
 
115
115
  self.redirect_uri = redirect_uri or os.environ.get("SWEATSTACK_REDIRECT_URI")
116
116
 
117
+ self._proxy_mode = False
118
+ self._logout_uri = None
119
+
117
120
  self.api_key = st.session_state.get("sweatstack_api_key")
118
121
  self.refresh_token = st.session_state.get("sweatstack_refresh_token")
119
122
  self.client = Client(
@@ -124,13 +127,97 @@ class StreamlitAuth:
124
127
  client_secret=self.client_secret,
125
128
  )
126
129
 
130
+ @classmethod
131
+ def behind_proxy(
132
+ cls,
133
+ redirect_uri: str,
134
+ header_name: str = "X-SweatStack-Token",
135
+ logout_uri: str = "/logout",
136
+ ) -> "StreamlitAuth":
137
+ """Create a StreamlitAuth instance for use behind a proxy.
138
+
139
+ Use this method when your Streamlit app runs behind a proxy that handles
140
+ authentication and passes the SweatStack access token via an HTTP header.
141
+
142
+ Args:
143
+ redirect_uri: The URI to redirect to after login (used by proxy).
144
+ header_name: The HTTP header name containing the access token.
145
+ Defaults to "X-SweatStack-Token".
146
+ logout_uri: The URI to redirect to for logout.
147
+ Defaults to "/logout".
148
+
149
+ Returns:
150
+ StreamlitAuth: An instance configured for proxy mode.
151
+
152
+ Example:
153
+ auth = StreamlitAuth.behind_proxy(
154
+ redirect_uri="https://myapp.example.com/app",
155
+ )
156
+
157
+ if not auth.is_authenticated():
158
+ st.error("Missing authentication header")
159
+ st.stop()
160
+
161
+ activities = auth.client.get_activities()
162
+ """
163
+ instance = cls(redirect_uri=redirect_uri)
164
+ instance._proxy_mode = True
165
+ instance._logout_uri = logout_uri
166
+
167
+ token = st.context.headers.get(header_name)
168
+ if token:
169
+ instance.api_key = token
170
+ instance.client = Client(token, streamlit_compatible=True)
171
+
172
+ return instance
173
+
174
+ def _show_styled_link_button(self, label: str, url: str):
175
+ """Displays a styled link button with hover effects.
176
+
177
+ Args:
178
+ label: Text to display on the button.
179
+ url: The URL to navigate to when clicked.
180
+ """
181
+ st.markdown(
182
+ f"""
183
+ <style>
184
+ .animated-button {{
185
+ }}
186
+ .animated-button:hover {{
187
+ transform: scale(1.05);
188
+ }}
189
+ .animated-button:active {{
190
+ transform: scale(1);
191
+ }}
192
+ </style>
193
+ <a href="{url}"
194
+ target="_top"
195
+ class="animated-button"
196
+ style="display: inline-block;
197
+ padding: 10px 20px;
198
+ background-color: #EF2B2D;
199
+ color: white;
200
+ text-decoration: none;
201
+ border-radius: 6px;
202
+ border: none;
203
+ transition: all 0.3s ease;
204
+ cursor: pointer;"
205
+ >{label}</a>
206
+ """,
207
+ unsafe_allow_html=True,
208
+ )
209
+
127
210
  def logout_button(self):
128
211
  """Displays a logout button and handles user logout.
129
212
 
130
- When clicked, this button clears the stored API key from session state,
131
- resets the client, and triggers a Streamlit rerun to update the UI.
213
+ In standard mode, clears the stored API key from session state,
214
+ resets the client, and triggers a Streamlit rerun.
215
+
216
+ In proxy mode, displays a styled link that redirects to the logout URI.
132
217
  """
133
- if st.button("Logout"):
218
+ if self._proxy_mode:
219
+ self._show_styled_link_button("Logout", self._logout_uri)
220
+ elif st.button("Logout"):
134
221
  self.api_key = None
135
222
  self.refresh_token = None
136
223
  self.client = Client(streamlit_compatible=True)
@@ -151,34 +238,7 @@ class StreamlitAuth:
151
238
  authorization_url = self.get_authorization_url()
152
239
  login_label = login_label or "Connect with SweatStack"
153
240
  if not self._running_on_streamlit_cloud():
154
- st.markdown(
155
- f"""
156
- <style>
157
- .animated-button {{
158
- }}
159
- .animated-button:hover {{
160
- transform: scale(1.05);
161
- }}
162
- .animated-button:active {{
163
- transform: scale(1);
164
- }}
165
- </style>
166
- <a href="{authorization_url}"
167
- target="_top"
168
- class="animated-button"
169
- style="display: inline-block;
170
- padding: 10px 20px;
171
- background-color: #EF2B2D;
172
- color: white;
173
- text-decoration: none;
174
- border-radius: 6px;
175
- border: none;
176
- transition: all 0.3s ease;
177
- cursor: pointer;"
178
- >{login_label}</a>
179
- """,
180
- unsafe_allow_html=True,
181
- )
241
+ self._show_styled_link_button(login_label, authorization_url)
182
242
  else:
183
243
  st.link_button(login_label, authorization_url)
184
244
 
@@ -275,12 +335,23 @@ class StreamlitAuth:
275
335
  to the Streamlit app with an authorization code, which is exchanged for an
276
336
  access token.
277
337
 
338
+ In proxy mode, this method only shows the login button if not authenticated.
339
+ The proxy handles the OAuth callback and token exchange.
340
+
278
341
  Args:
279
342
  login_label: The label to display on the login button. Defaults to "Login with SweatStack".
280
343
 
281
344
  Returns:
282
345
  None
283
346
  """
347
+ if self._proxy_mode:
348
+ if self.is_authenticated():
349
+ if show_logout:
350
+ self.logout_button()
351
+ else:
352
+ self._show_sweatstack_login(login_label)
353
+ return
354
+
284
355
  if self.is_authenticated():
285
356
  if not st.session_state.get("sweatstack_auth_toast_shown", False):
286
357
  st.toast("SweatStack authentication successful!", icon="✅")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.56.0
3
+ Version: 0.57.0
4
4
  Summary: The official Python client for SweatStack
5
5
  Author-email: Aart Goossens <aart@gssns.io>
6
6
  Requires-Python: >=3.9
@@ -7,11 +7,11 @@ sweatstack/jupyterlab_oauth2_startup.py,sha256=YcjXvzeZ459vL_dCkFi1IxX_RNAu80ZX9
7
7
  sweatstack/openapi_schemas.py,sha256=VvquBdbssdB9D1KeJYQCx51hy1Df4SS0PjzGWXcUaew,46221
8
8
  sweatstack/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  sweatstack/schemas.py,sha256=Xh9E8DjFx5NIEBnVqS6ixFVb0E06ANZbdOlnMofCpZw,4481
10
- sweatstack/streamlit.py,sha256=fVTgTyb8a2c7IC-lCs32ocILvyj1dFXP4iQaTV9D4Is,17287
10
+ sweatstack/streamlit.py,sha256=wnabWhife9eMAdkECPjRKkzE82KZoi_H8YzucZl_m9s,19604
11
11
  sweatstack/sweatshell.py,sha256=MYLNcWbOdceqKJ3S0Pe8dwHXEeYsGJNjQoYUXpMTftA,333
12
12
  sweatstack/utils.py,sha256=AwHRdC1ziOZ5o9RBIB21Uxm-DoClVRAJSVvgsmSmvps,1801
13
13
  sweatstack/Sweat Stack examples/Getting started.ipynb,sha256=k2hiSffWecoQ0VxjdpDcgFzBXDQiYEebhnAYlu8cgX8,6335204
14
- sweatstack-0.56.0.dist-info/METADATA,sha256=N732fwIaJz4h5uVfOCZUwSaMbaLk7CB1leB6dHpvpw4,852
15
- sweatstack-0.56.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- sweatstack-0.56.0.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
17
- sweatstack-0.56.0.dist-info/RECORD,,
14
+ sweatstack-0.57.0.dist-info/METADATA,sha256=0amcxzS2bzmC9qy3v_9wZ9_DH2BBAkqfvN8gECa7BZ0,852
15
+ sweatstack-0.57.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ sweatstack-0.57.0.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
17
+ sweatstack-0.57.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any