sweatstack 0.56.0__py3-none-any.whl → 0.58.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/openapi_schemas.py +135 -41
- sweatstack/streamlit.py +102 -31
- {sweatstack-0.56.0.dist-info → sweatstack-0.58.0.dist-info}/METADATA +1 -1
- {sweatstack-0.56.0.dist-info → sweatstack-0.58.0.dist-info}/RECORD +6 -6
- {sweatstack-0.56.0.dist-info → sweatstack-0.58.0.dist-info}/WHEEL +1 -1
- {sweatstack-0.56.0.dist-info → sweatstack-0.58.0.dist-info}/entry_points.txt +0 -0
sweatstack/openapi_schemas.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by datamodel-codegen:
|
|
2
2
|
# filename: openapi.json
|
|
3
|
-
# timestamp:
|
|
3
|
+
# timestamp: 2026-01-24T14:09:07+00:00
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
@@ -22,13 +22,33 @@ from pydantic import (
|
|
|
22
22
|
|
|
23
23
|
class Activity(BaseModel):
|
|
24
24
|
id: str = Field(..., title='Id')
|
|
25
|
-
start_date_local: datetime = Field(
|
|
25
|
+
start_date_local: Optional[datetime] = Field(None, title='Start Date Local')
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class ActivityUpdate(BaseModel):
|
|
29
29
|
tags: Optional[List[str]] = Field(None, title='Tags')
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
class AltitudeSummary(BaseModel):
|
|
33
|
+
mean: Optional[float] = Field(None, title='Mean')
|
|
34
|
+
gain: Optional[Union[int, float]] = Field(None, title='Gain')
|
|
35
|
+
loss: Optional[Union[int, float]] = Field(None, title='Loss')
|
|
36
|
+
min: Optional[Union[int, float]] = Field(None, title='Min')
|
|
37
|
+
max: Optional[Union[int, float]] = Field(None, title='Max')
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ApplicationCreateOrUpdate(BaseModel):
|
|
41
|
+
name: str = Field(..., title='Name')
|
|
42
|
+
description: Optional[str] = Field(None, title='Description')
|
|
43
|
+
url: Optional[AnyUrl] = Field(None, title='Url')
|
|
44
|
+
image: Optional[AnyUrl] = Field(None, title='Image')
|
|
45
|
+
redirect_uris: Optional[List[AnyUrl]] = Field(None, title='Redirect Uris')
|
|
46
|
+
privacy_statement: Optional[AnyUrl] = Field(None, title='Privacy Statement')
|
|
47
|
+
published: Optional[bool] = Field(False, title='Published')
|
|
48
|
+
webhook_endpoints: Optional[List[AnyUrl]] = Field(None, title='Webhook Endpoints')
|
|
49
|
+
webhook_secret: Optional[SecretStr] = Field(None, title='Webhook Secret')
|
|
50
|
+
|
|
51
|
+
|
|
32
52
|
class Prompt(Enum):
|
|
33
53
|
none = 'none'
|
|
34
54
|
login = 'login'
|
|
@@ -36,6 +56,10 @@ class Prompt(Enum):
|
|
|
36
56
|
select_account = 'select_account'
|
|
37
57
|
|
|
38
58
|
|
|
59
|
+
class BackfillError(BaseModel):
|
|
60
|
+
error: str = Field(..., title='Error')
|
|
61
|
+
|
|
62
|
+
|
|
39
63
|
class BackfillStatus(BaseModel):
|
|
40
64
|
backfill_loaded_until: Optional[datetime] = Field(
|
|
41
65
|
..., title='Backfill Loaded Until'
|
|
@@ -46,16 +70,6 @@ class BodyAddEmailPartialsAddEmailPost(BaseModel):
|
|
|
46
70
|
email: EmailStr = Field(..., title='Email')
|
|
47
71
|
|
|
48
72
|
|
|
49
|
-
class BodyCreateApplicationApplicationsPost(BaseModel):
|
|
50
|
-
name: str = Field(..., title='Name')
|
|
51
|
-
description: str = Field(..., title='Description')
|
|
52
|
-
url: AnyUrl = Field(..., title='Url')
|
|
53
|
-
image: AnyUrl = Field(..., title='Image')
|
|
54
|
-
redirect_uris: List[str] = Field(..., title='Redirect Uris')
|
|
55
|
-
privacy_statement: AnyUrl = Field(..., title='Privacy Statement')
|
|
56
|
-
published: Optional[bool] = Field(False, title='Published')
|
|
57
|
-
|
|
58
|
-
|
|
59
73
|
class BodyCreateApplicationSecretApplicationsApplicationIdSecretsPost(BaseModel):
|
|
60
74
|
label: str = Field(..., title='Label')
|
|
61
75
|
|
|
@@ -668,15 +682,23 @@ class Tz(Enum):
|
|
|
668
682
|
|
|
669
683
|
class BodyLoginPostLoginPost(BaseModel):
|
|
670
684
|
email: EmailStr = Field(..., title='Email')
|
|
671
|
-
|
|
672
|
-
tz: Tz = Field(..., title='Tz')
|
|
685
|
+
tz: Optional[Tz] = Field('Europe/Oslo', title='Tz')
|
|
673
686
|
state: Optional[str] = Field(None, title='State')
|
|
674
687
|
|
|
675
688
|
|
|
676
|
-
class
|
|
689
|
+
class BodyPostCreateManagedUserSettingsManagedUsersPost(BaseModel):
|
|
690
|
+
first_name: str = Field(..., title='First Name')
|
|
691
|
+
last_name: Optional[str] = Field(None, title='Last Name')
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
class BodyPutUpdateManagedUserSettingsManagedUsersUserIdPut(BaseModel):
|
|
695
|
+
first_name: str = Field(..., title='First Name')
|
|
696
|
+
last_name: Optional[str] = Field(None, title='Last Name')
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
class BodyResendMagicLinkPartialsResendMagicLinkPost(BaseModel):
|
|
677
700
|
email: EmailStr = Field(..., title='Email')
|
|
678
|
-
|
|
679
|
-
tz: Tz = Field(..., title='Tz')
|
|
701
|
+
tz: Optional[Tz] = Field('Europe/Oslo', title='Tz')
|
|
680
702
|
|
|
681
703
|
|
|
682
704
|
class BodySaveOrUpdateIntegrationProviderTenantsTenantIdIntegrationProvidersIntegrationNamePost(
|
|
@@ -687,19 +709,9 @@ class BodySaveOrUpdateIntegrationProviderTenantsTenantIdIntegrationProvidersInte
|
|
|
687
709
|
redirect_url: str = Field(..., title='Redirect Url')
|
|
688
710
|
|
|
689
711
|
|
|
690
|
-
class
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
class BodyUpdateApplicationApplicationsApplicationIdPut(BaseModel):
|
|
696
|
-
name: str = Field(..., title='Name')
|
|
697
|
-
description: str = Field(..., title='Description')
|
|
698
|
-
url: AnyUrl = Field(..., title='Url')
|
|
699
|
-
image: AnyUrl = Field(..., title='Image')
|
|
700
|
-
redirect_uris: List[str] = Field(..., title='Redirect Uris')
|
|
701
|
-
privacy_statement: AnyUrl = Field(..., title='Privacy Statement')
|
|
702
|
-
published: Optional[bool] = Field(False, title='Published')
|
|
712
|
+
class BodyUpdateManagedUserApiV1UsersUserIdPut(BaseModel):
|
|
713
|
+
first_name: Optional[str] = Field(None, title='First Name')
|
|
714
|
+
last_name: Optional[str] = Field(None, title='Last Name')
|
|
703
715
|
|
|
704
716
|
|
|
705
717
|
class BodyUpdateUserUsersUserIdPut(BaseModel):
|
|
@@ -715,6 +727,11 @@ class BodyUploadActivityFileDataUploadPost(BaseModel):
|
|
|
715
727
|
files: List[bytes] = Field(..., title='Files')
|
|
716
728
|
|
|
717
729
|
|
|
730
|
+
class CadenceSummary(BaseModel):
|
|
731
|
+
mean: Optional[float] = Field(None, title='Mean')
|
|
732
|
+
max: Optional[float] = Field(None, title='Max')
|
|
733
|
+
|
|
734
|
+
|
|
718
735
|
class CoreTemperatureSummary(BaseModel):
|
|
719
736
|
mean: Optional[float] = Field(None, title='Mean')
|
|
720
737
|
min: Optional[float] = Field(None, title='Min')
|
|
@@ -724,12 +741,7 @@ class CoreTemperatureSummary(BaseModel):
|
|
|
724
741
|
|
|
725
742
|
|
|
726
743
|
class DistanceSummary(BaseModel):
|
|
727
|
-
sum: Optional[int] = Field(None, title='Sum')
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
class ElevationSummary(BaseModel):
|
|
731
|
-
min: Optional[int] = Field(None, title='Min')
|
|
732
|
-
max: Optional[int] = Field(None, title='Max')
|
|
744
|
+
sum: Optional[Union[int, float]] = Field(None, title='Sum')
|
|
733
745
|
|
|
734
746
|
|
|
735
747
|
class GarminBackfillBody(BaseModel):
|
|
@@ -739,6 +751,11 @@ class GarminBackfillBody(BaseModel):
|
|
|
739
751
|
user_id__not_in: Optional[List[str]] = Field(None, title='User Id Not In')
|
|
740
752
|
|
|
741
753
|
|
|
754
|
+
class GarminConnectUserPermission(Enum):
|
|
755
|
+
HISTORICAL_DATA_EXPORT = 'HISTORICAL_DATA_EXPORT'
|
|
756
|
+
ACTIVITY_EXPORT = 'ACTIVITY_EXPORT'
|
|
757
|
+
|
|
758
|
+
|
|
742
759
|
class GarminDeregistration(BaseModel):
|
|
743
760
|
userId: str = Field(..., title='Userid')
|
|
744
761
|
userAccessToken: str = Field(..., title='Useraccesstoken')
|
|
@@ -754,6 +771,18 @@ class GarminFileTypes(Enum):
|
|
|
754
771
|
TCX = 'TCX'
|
|
755
772
|
|
|
756
773
|
|
|
774
|
+
class GarminUserPermissionChange(BaseModel):
|
|
775
|
+
userId: str = Field(..., title='Userid')
|
|
776
|
+
userAccessToken: str = Field(..., title='Useraccesstoken')
|
|
777
|
+
permissions: List[GarminConnectUserPermission] = Field(..., title='Permissions')
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
class GarminUserPermissionsBody(BaseModel):
|
|
781
|
+
userPermissionsChange: List[GarminUserPermissionChange] = Field(
|
|
782
|
+
..., title='Userpermissionschange'
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
|
|
757
786
|
class GrantType(Enum):
|
|
758
787
|
authorization_code = 'authorization_code'
|
|
759
788
|
refresh_token = 'refresh_token'
|
|
@@ -796,6 +825,19 @@ class IntervalsIcuWebhookEventType(Enum):
|
|
|
796
825
|
FITNESS_UPDATED = 'FITNESS_UPDATED'
|
|
797
826
|
|
|
798
827
|
|
|
828
|
+
class ManagedUserCreate(BaseModel):
|
|
829
|
+
first_name: str = Field(..., title='First Name')
|
|
830
|
+
last_name: Optional[str] = Field(..., title='Last Name')
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
class MetabolicMapResponse(BaseModel):
|
|
834
|
+
fatmax: float = Field(..., title='Fatmax')
|
|
835
|
+
mlss: float = Field(..., title='Mlss')
|
|
836
|
+
cp: float = Field(..., title='Cp')
|
|
837
|
+
vo2max: float = Field(..., title='Vo2Max')
|
|
838
|
+
vo2max_intensity: float = Field(..., title='Vo2Max Intensity')
|
|
839
|
+
|
|
840
|
+
|
|
799
841
|
class Metric(Enum):
|
|
800
842
|
duration = 'duration'
|
|
801
843
|
lap = 'lap'
|
|
@@ -806,6 +848,7 @@ class Metric(Enum):
|
|
|
806
848
|
smo2 = 'smo2'
|
|
807
849
|
core_temperature = 'core_temperature'
|
|
808
850
|
elevation = 'elevation'
|
|
851
|
+
altitude = 'altitude'
|
|
809
852
|
cadence = 'cadence'
|
|
810
853
|
temperature = 'temperature'
|
|
811
854
|
distance = 'distance'
|
|
@@ -818,7 +861,6 @@ class Metric(Enum):
|
|
|
818
861
|
|
|
819
862
|
class PowerSummary(BaseModel):
|
|
820
863
|
mean: Optional[float] = Field(None, title='Mean')
|
|
821
|
-
min: Optional[float] = Field(None, title='Min')
|
|
822
864
|
max: Optional[float] = Field(None, title='Max')
|
|
823
865
|
|
|
824
866
|
|
|
@@ -850,7 +892,6 @@ class Smo2Summary(BaseModel):
|
|
|
850
892
|
|
|
851
893
|
class SpeedSummary(BaseModel):
|
|
852
894
|
mean: Optional[float] = Field(None, title='Mean')
|
|
853
|
-
min: Optional[float] = Field(None, title='Min')
|
|
854
895
|
max: Optional[float] = Field(None, title='Max')
|
|
855
896
|
|
|
856
897
|
|
|
@@ -897,10 +938,28 @@ class Sport(Enum):
|
|
|
897
938
|
unknown = 'unknown'
|
|
898
939
|
|
|
899
940
|
|
|
941
|
+
class SubscriptionPlan(Enum):
|
|
942
|
+
free = 'free'
|
|
943
|
+
early_access = 'early_access'
|
|
944
|
+
individual = 'individual'
|
|
945
|
+
developer = 'developer'
|
|
946
|
+
team = 'team'
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
class TeamCreateOrUpdate(BaseModel):
|
|
950
|
+
name: str = Field(..., title='Name')
|
|
951
|
+
description: str = Field(..., title='Description')
|
|
952
|
+
url: AnyUrl = Field(..., title='Url')
|
|
953
|
+
image: AnyUrl = Field(..., title='Image')
|
|
954
|
+
privacy_statement: AnyUrl = Field(..., title='Privacy Statement')
|
|
955
|
+
|
|
956
|
+
|
|
900
957
|
class TemperatureSummary(BaseModel):
|
|
901
958
|
mean: Optional[float] = Field(None, title='Mean')
|
|
902
959
|
min: Optional[float] = Field(None, title='Min')
|
|
903
960
|
max: Optional[float] = Field(None, title='Max')
|
|
961
|
+
start: Optional[float] = Field(None, title='Start')
|
|
962
|
+
end: Optional[float] = Field(None, title='End')
|
|
904
963
|
|
|
905
964
|
|
|
906
965
|
class TemplateDay(BaseModel):
|
|
@@ -965,12 +1024,23 @@ class UserInfoResponse(BaseModel):
|
|
|
965
1024
|
name: str = Field(..., title='Name')
|
|
966
1025
|
|
|
967
1026
|
|
|
1027
|
+
class UserResponse(BaseModel):
|
|
1028
|
+
id: str = Field(..., title='Id')
|
|
1029
|
+
first_name: Optional[str] = Field(..., title='First Name')
|
|
1030
|
+
last_name: Optional[str] = Field(..., title='Last Name')
|
|
1031
|
+
admin: bool = Field(..., title='Admin')
|
|
1032
|
+
registered_at: datetime = Field(..., title='Registered At')
|
|
1033
|
+
display_name: str = Field(..., title='Display Name')
|
|
1034
|
+
is_managed: bool = Field(..., title='Is Managed')
|
|
1035
|
+
|
|
1036
|
+
|
|
968
1037
|
class UserSummary(BaseModel):
|
|
969
1038
|
id: str = Field(..., title='Id')
|
|
970
1039
|
first_name: Optional[str] = Field(..., title='First Name')
|
|
971
1040
|
last_name: Optional[str] = Field(..., title='Last Name')
|
|
972
1041
|
scopes: List[Scope] = Field(..., title='Scopes')
|
|
973
1042
|
display_name: str = Field(..., title='Display Name')
|
|
1043
|
+
is_managed: bool = Field(..., title='Is Managed')
|
|
974
1044
|
|
|
975
1045
|
|
|
976
1046
|
class ValidationError(BaseModel):
|
|
@@ -990,12 +1060,19 @@ class VolumeQuantity(Enum):
|
|
|
990
1060
|
distance = 'distance'
|
|
991
1061
|
|
|
992
1062
|
|
|
1063
|
+
class WebhookEventType(Enum):
|
|
1064
|
+
activity_created = 'activity_created'
|
|
1065
|
+
activity_updated = 'activity_updated'
|
|
1066
|
+
activity_deleted = 'activity_deleted'
|
|
1067
|
+
|
|
1068
|
+
|
|
993
1069
|
class ActivitySummarySummary(BaseModel):
|
|
994
1070
|
power: Optional[PowerSummary] = None
|
|
995
1071
|
speed: Optional[SpeedSummary] = None
|
|
996
1072
|
distance: Optional[DistanceSummary] = None
|
|
997
|
-
|
|
1073
|
+
altitude: Optional[AltitudeSummary] = None
|
|
998
1074
|
heart_rate: Optional[HeartRateSummary] = None
|
|
1075
|
+
cadence: Optional[CadenceSummary] = None
|
|
999
1076
|
temperature: Optional[TemperatureSummary] = None
|
|
1000
1077
|
core_temperature: Optional[CoreTemperatureSummary] = None
|
|
1001
1078
|
smo2: Optional[Smo2Summary] = None
|
|
@@ -1013,6 +1090,10 @@ class AuthorizeQueryParams(BaseModel):
|
|
|
1013
1090
|
user_flow: Optional[UserFlow] = 'session'
|
|
1014
1091
|
|
|
1015
1092
|
|
|
1093
|
+
class AuthorizeTeamRequest(BaseModel):
|
|
1094
|
+
scopes: List[Scope] = Field(..., title='Scopes')
|
|
1095
|
+
|
|
1096
|
+
|
|
1016
1097
|
class BodyAuthorizeOauthAuthorizePost(BaseModel):
|
|
1017
1098
|
client_id: str = Field(..., title='Client Id')
|
|
1018
1099
|
redirect_uri: Optional[str] = Field(None, title='Redirect Uri')
|
|
@@ -1023,6 +1104,10 @@ class BodyAuthorizeOauthAuthorizePost(BaseModel):
|
|
|
1023
1104
|
code_challenge_method: Optional[str] = Field(None, title='Code Challenge Method')
|
|
1024
1105
|
|
|
1025
1106
|
|
|
1107
|
+
class BodyCreateCheckoutSessionApiV1PaymentStripeCheckoutPost(BaseModel):
|
|
1108
|
+
subscription_plan: SubscriptionPlan
|
|
1109
|
+
|
|
1110
|
+
|
|
1026
1111
|
class BodyExpressAddEmailPostExpressAddEmailPost(BaseModel):
|
|
1027
1112
|
email: EmailStr = Field(..., title='Email')
|
|
1028
1113
|
state: Optional[str] = Field(None, title='State')
|
|
@@ -1062,7 +1147,7 @@ class GarminActivityFileData(BaseModel):
|
|
|
1062
1147
|
callbackURL: str = Field(..., title='Callbackurl')
|
|
1063
1148
|
startTimeInSeconds: datetime = Field(..., title='Starttimeinseconds')
|
|
1064
1149
|
activityId: str = Field(..., title='Activityid')
|
|
1065
|
-
activityName: str = Field(
|
|
1150
|
+
activityName: Optional[str] = Field(None, title='Activityname')
|
|
1066
1151
|
manual: Optional[bool] = Field(False, title='Manual')
|
|
1067
1152
|
|
|
1068
1153
|
|
|
@@ -1085,8 +1170,9 @@ class Lap(BaseModel):
|
|
|
1085
1170
|
power: Optional[PowerSummary] = None
|
|
1086
1171
|
speed: Optional[SpeedSummary] = None
|
|
1087
1172
|
distance: Optional[DistanceSummary] = None
|
|
1088
|
-
|
|
1173
|
+
altitude: Optional[AltitudeSummary] = None
|
|
1089
1174
|
heart_rate: Optional[HeartRateSummary] = None
|
|
1175
|
+
cadence: Optional[CadenceSummary] = None
|
|
1090
1176
|
temperature: Optional[TemperatureSummary] = None
|
|
1091
1177
|
core_temperature: Optional[CoreTemperatureSummary] = None
|
|
1092
1178
|
smo2: Optional[Smo2Summary] = None
|
|
@@ -1133,6 +1219,13 @@ class RangeValueOutput(BaseModel):
|
|
|
1133
1219
|
max: Optional[Value] = None
|
|
1134
1220
|
|
|
1135
1221
|
|
|
1222
|
+
class WebhookEventBody(BaseModel):
|
|
1223
|
+
user_id: str = Field(..., title='User Id')
|
|
1224
|
+
event_type: WebhookEventType
|
|
1225
|
+
resource_id: Optional[str] = Field(..., title='Resource Id')
|
|
1226
|
+
timestamp: datetime = Field(..., title='Timestamp')
|
|
1227
|
+
|
|
1228
|
+
|
|
1136
1229
|
class IntervalInput(BaseModel):
|
|
1137
1230
|
type: Literal['interval'] = Field('interval', title='Type')
|
|
1138
1231
|
volume: Union[ConstantValueInput, RangeValueInput, RampValueInput] = Field(
|
|
@@ -1258,6 +1351,7 @@ class ActivityDetails(BaseModel):
|
|
|
1258
1351
|
laps: Optional[List[Lap]] = Field(None, title='Laps')
|
|
1259
1352
|
traces: Optional[List[TraceDetails]] = Field(None, title='Traces')
|
|
1260
1353
|
distance: Optional[float] = Field(None, title='Distance')
|
|
1354
|
+
devices: Optional[List[str]] = Field(None, title='Devices')
|
|
1261
1355
|
duration: timedelta = Field(..., title='Duration')
|
|
1262
1356
|
start_local: datetime = Field(..., title='Start Local')
|
|
1263
1357
|
end_local: datetime = Field(..., title='End Local')
|
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
|
-
|
|
131
|
-
resets the client, and triggers a Streamlit rerun
|
|
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
|
|
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
|
-
|
|
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="✅")
|
|
@@ -4,14 +4,14 @@ sweatstack/client.py,sha256=dTx7Cqpd56NH32-Ndc6vo1WnzsN1C9BfpFrchV9NTdQ,66366
|
|
|
4
4
|
sweatstack/constants.py,sha256=fGO6ksOv5HeISv9lHRoYm4besW1GTveXS8YD3K0ljg0,41
|
|
5
5
|
sweatstack/ipython_init.py,sha256=OtBB9dQvyLXklD4kA2x1swaVtU9u73fG4V4-zz4YRAg,139
|
|
6
6
|
sweatstack/jupyterlab_oauth2_startup.py,sha256=YcjXvzeZ459vL_dCkFi1IxX_RNAu80ZX9rwa0OXJfTM,1023
|
|
7
|
-
sweatstack/openapi_schemas.py,sha256=
|
|
7
|
+
sweatstack/openapi_schemas.py,sha256=iXxhnEVErlgapNnXWbb0bJRWwiGBUMXO7sdovL5q8U8,49475
|
|
8
8
|
sweatstack/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
sweatstack/schemas.py,sha256=Xh9E8DjFx5NIEBnVqS6ixFVb0E06ANZbdOlnMofCpZw,4481
|
|
10
|
-
sweatstack/streamlit.py,sha256=
|
|
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.
|
|
15
|
-
sweatstack-0.
|
|
16
|
-
sweatstack-0.
|
|
17
|
-
sweatstack-0.
|
|
14
|
+
sweatstack-0.58.0.dist-info/METADATA,sha256=oiwc1kDkz9wiwYZdYm4QaY1PhMEBLTUi1e4MRwjx-84,852
|
|
15
|
+
sweatstack-0.58.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
sweatstack-0.58.0.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
|
|
17
|
+
sweatstack-0.58.0.dist-info/RECORD,,
|
|
File without changes
|