16 Commits

Author SHA1 Message Date
0611ceb3ec Release v26.3.16
All checks were successful
Production deployment / Build (release) Successful in 1m4s
Staging deployment / Build (release) Successful in 1m24s
CI / Checks (push) Successful in 4m36s
Staging deployment / Deploy (release) Successful in 2m16s
Production deployment / Deploy (release) Successful in 3m1s
2026-03-16 20:16:07 +01:00
3c71464663 BTHLABS-82: Spring 2026 Refresh
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2026-03-16 19:09:38 +00:00
c842657766 BTHLABS-83: View association view raises 403 when opened as anonymous
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2026-03-12 16:26:54 +00:00
e2b2455bea BTHLABS-0000: Add build-opera-source task in _extension_. 2026-01-15 20:54:37 +01:00
1b775a130b Release 26.1.14.post0
All checks were successful
Production deployment / Build (release) Successful in 1m58s
Staging deployment / Build (release) Successful in 2m0s
CI / Checks (push) Successful in 4m43s
Staging deployment / Deploy (release) Successful in 1m56s
Production deployment / Deploy (release) Successful in 2m25s
2026-01-14 21:24:09 +01:00
bcef0d2d09 BTHLABS-0000: Fixed DB host resolution post VPN migration 2026-01-14 21:23:15 +01:00
9f121a0ceb Release v26.1.14
Some checks failed
CI / Checks (push) Successful in 1m50s
Production deployment / Build (release) Successful in 58s
Staging deployment / Build (release) Successful in 59s
Staging deployment / Deploy (release) Successful in 1m45s
Production deployment / Deploy (release) Failing after 4m29s
2026-01-14 20:59:08 +01:00
d16f0cd957 BTHLABS-81: OG Properties on Share Association page
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2026-01-14 20:53:46 +01:00
98e5e1891a Release v25.12.04
All checks were successful
CI / Checks (push) Successful in 2m14s
Production deployment / Build (release) Successful in 26s
Staging deployment / Build (release) Successful in 50s
Staging deployment / Deploy (release) Successful in 1m19s
Production deployment / Deploy (release) Successful in 2m17s
2025-12-04 20:57:48 +01:00
06343e6ed3 BTHLABS-0000: Tweaking association card's layout 2025-12-04 20:55:55 +01:00
82a3b612ec BTHLABS-0000: Fix YT embed code 2025-12-04 20:46:38 +01:00
1e549d3fc2 Release v25.11.26
All checks were successful
Production deployment / Build (release) Successful in 32s
CI / Checks (push) Successful in 2m20s
Staging deployment / Build (release) Successful in 1m9s
Staging deployment / Deploy (release) Successful in 1m10s
Production deployment / Deploy (release) Successful in 2m25s
2025-11-27 17:52:16 +01:00
55126f4af6 BTHLABS-66: Prepping for public release: Take five
AKA "Using Apple reviewers as QA for your project". Thanks, y'all! :)
2025-11-27 17:51:19 +01:00
cca49f2292 BTHLABS-66: Prepping for public release: Take four
Apple gonna Apple... ;)
2025-11-25 12:54:52 +01:00
23f8296659 BTHLABS-66: Prepping for public release: Take three
I smell a drastic change to auth flow in the Mac app... Let's see if it
gets approved this time :D.
2025-11-21 21:04:28 +01:00
9abed01e53 BTHLABS-0000: Code cleanup 2025-11-20 20:34:42 +01:00
73 changed files with 1357 additions and 394 deletions

View File

@@ -1,6 +1,23 @@
# HotPocket by BTHLabs
This repository contains the _HotPocket_ project.
Minimal self-hosted bookmarking app :).
## The what, the why and the ugly
HotPocket is a minimal self-hosted bookmarking app. It combines a Web
application, companion apps, browser extensions to give you a way to quickly
save links for later.
HotPocket came to be to fill in the blank left by Pocket, after Mozilla shut it
down. I looked at the existing alternatives and found them either too
feature-rich, too involved to self-host or otherwise not to my liking. So I
decided to sit down and build something for myself.
With the what and why out of the way, let's talk about the ugly... At its core
HotPocket is a personal project. I built it by myself and for myself. It may
or may not fit your needs. If it does, happy saving!
If you're feeling up for an adventure, continue reading below :).
## Development setup
@@ -66,7 +83,7 @@ $ docker run --rm -it \
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME=hotpocket \
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD=hotpocketm4st3r \
-p 8000:8000 \
hotpocket/backend:aio-v25.11.19-01
hotpocket/backend:aio-v26.3.16-01
```
The command above will set up and start the application. The SQLite file will
@@ -128,6 +145,7 @@ that can be used to configure the services.
| `HOTPOCKET_BACKEND_RUN_MIGRATIONS` | `false` or `true` | Set to `true` to run database muigrations when the container starts. |
| `HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME` | N/A | Username for the initial account. |
| `HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD` | N/A | Password for the initial account. |
| `HOTPOCKET_BACKEND_OPERATOR_EMAIL` | N/A | Instance operator's e-mail. Used to display extra language on login page. |
**Env and App settings**
@@ -158,6 +176,15 @@ method's name in the UI and defaults to `OIDC`.
**NOTE:** Currently, only Keycloak has been tested with this login method.
### Volumes
Both images declare `/srv/run` to be a volume. It's intended to keep the
service's runtime data, including but not limited to PID files, UNIX sockets
etc. It's recommended to persist this volume.
Additionally, the `deployment` image declares `/srv/uploads` to be a volume.
It's recommeded to persist this volume.
## Author
_HotPocket_ is developed by [BTHLabs](https://www.bthlabs.pl/).

View File

@@ -1,6 +1,6 @@
services:
backend:
image: "hotpocket/backend:aio-v25.11.19-01"
image: "bthlabs/hotpocket:aio-v26.3.16-01"
environment:
HOTPOCKET_BACKEND_SECRET_KEY: "thisisntright"
HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME: "hotpocket"

View File

@@ -8,7 +8,7 @@ x-backend-environment: &x-backend-environment
services:
webapp:
image: "hotpocket/backend:deployment-v25.11.19-01"
image: "bthlabs/hotpocket:deployment-v26.3.16-01"
environment:
<<: *x-backend-environment
HOTPOCKET_BACKEND_ALLOWED_HOSTS: "app.staging.hotpocket.bthlab.bthlabs.net"
@@ -21,7 +21,7 @@ services:
restart: "unless-stopped"
admin:
image: "hotpocket/backend:deployment-v25.11.19-01"
image: "bthlabs/hotpocket:deployment-v26.3.16-01"
environment:
<<: *x-backend-environment
HOTPOCKET_BACKEND_APP: "admin"
@@ -35,7 +35,7 @@ services:
restart: "unless-stopped"
celery-worker:
image: "hotpocket/backend:deployment-v25.11.19-01"
image: "bthlabs/hotpocket:deployment-v26.3.16-01"
command:
- "/srv/venv/bin/celery"
- "-A"
@@ -57,7 +57,7 @@ services:
restart: "unless-stopped"
celery-beat:
image: "hotpocket/backend:deployment-v25.11.19-01"
image: "bthlabs/hotpocket:deployment-v26.3.16-01"
command:
- "/srv/venv/bin/celery"
- "-A"

View File

@@ -8,7 +8,7 @@ hotpocket_app:
node: "home.vm.snakeweb.net"
docker:
extra_hosts:
- "home.vm:10.0.1.2"
- "home.vm:10.16.1.100"
backend:
image_tag: "{{ hotpocket_app_image_tag|default('deployment-v25.10.21-01') }}"
database:

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "hotpocket-workspace"
version = "25.11.19"
version = "26.3.16"
description = "HotPocket Workspace"
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
license = "Apache-2.0"

View File

@@ -92,3 +92,5 @@ Shared (Extension)/Resources/content-bundle.js
Shared (Extension)/Resources/manifest.json
Shared (Extension)/Resources/preauth.html
Shared (Extension)/Resources/preauth.js
Shared (Extension)/Resources/options.html
Shared (Extension)/Resources/options.js

View File

@@ -185,6 +185,8 @@
"Resources/content-bundle.js",
Resources/images,
Resources/manifest.json,
Resources/options.html,
Resources/options.js,
Resources/preauth.html,
Resources/preauth.js,
SafariWebExtensionHandler.m,
@@ -199,6 +201,8 @@
"Resources/content-bundle.js",
Resources/images,
Resources/manifest.json,
Resources/options.html,
Resources/options.js,
Resources/preauth.html,
Resources/preauth.js,
SafariWebExtensionHandler.m,
@@ -718,7 +722,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Share Extension)/Info.plist";
@@ -731,7 +735,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket";
SDKROOT = iphoneos;
@@ -751,7 +755,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Share Extension)/Info.plist";
@@ -764,7 +768,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket";
SDKROOT = iphoneos;
@@ -784,7 +788,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
@@ -797,7 +801,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -819,7 +823,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
@@ -832,7 +836,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -858,7 +862,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "iOS (App)/HotPocket (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (App)/Info.plist";
@@ -878,7 +882,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -904,7 +908,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "iOS (App)/HotPocket (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (App)/Info.plist";
@@ -924,7 +928,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -950,7 +954,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
@@ -965,7 +969,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -985,7 +989,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
@@ -1000,7 +1004,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -1022,7 +1026,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1038,7 +1042,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -1061,7 +1065,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1077,7 +1081,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
OTHER_LDFLAGS = (
"-framework",
SafariServices,
@@ -1211,7 +1215,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1225,7 +1229,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket";
REGISTER_APP_GROUPS = YES;
@@ -1241,7 +1245,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2025111901;
CURRENT_PROJECT_VERSION = 2026031601;
DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -1255,7 +1259,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.11.19;
MARKETING_VERSION = 26.3.16;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket";
REGISTER_APP_GROUPS = YES;

View File

@@ -10,6 +10,7 @@
#import "HPAPI.h"
#import "HPCredentialsHelper.h"
#import "HPRPCClient.h"
#import "NSBundle+HotPocketExtensions.h"
#import "NSURL+HotPocketExtensions.h"
@implementation HPAuthParams
@@ -77,18 +78,19 @@
return nil;
}
NSDictionary *postAuthenticateURLParams = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"HPAuthFlowPostAuthenticateURLParts"];
if (postAuthenticateURLParams == nil) {
NSString *expectedScheme = [NSBundle postAuthenticateURLScheme];
NSString *expectedHost = [NSBundle postAuthenticateURLHost];
if (expectedScheme == nil || expectedHost == nil) {
return nil;
}
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
if ([urlComponents.scheme isEqualToString:[postAuthenticateURLParams valueForKey:@"scheme"]] == NO) {
if ([urlComponents.scheme isEqualToString:expectedScheme] == NO) {
return nil;
}
if ([urlComponents.host isEqualToString:[postAuthenticateURLParams valueForKey:@"host"]] == NO) {
if ([urlComponents.host isEqualToString:expectedHost] == NO) {
return nil;
}
@@ -109,6 +111,8 @@
}
-(BOOL)handleAuthParams:(HPAuthParams *)authParams {
[[NSNotificationCenter defaultCenter] postNotificationName:@"AuthFlowDidReceiveAuthParams" object:self];
HPRPCClient *rpcClient = [[HPRPCClient alloc] initWithBaseURL:self.baseURL accessToken:nil];
NSArray *callParams = @[
@@ -120,7 +124,7 @@
method:@"accounts.access_tokens.create"
params:callParams endopoint:@"/accounts/rpc/"
completionHandler:^(NSString *callId, HPRPCCallResult *result) {
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (result.error != nil) {
NSLog(@"-[HPAuthFlow handleAuthParams:] error=`%@`", result.error);
} else {

View File

@@ -12,6 +12,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface NSBundle (HotPocketExtensions)
+(NSString *)uname;
+(NSDictionary *)postAuthenticateURLParams;
+(NSString *)postAuthenticateURLScheme;
+(NSString *)postAuthenticateURLHost;
@end

View File

@@ -14,4 +14,23 @@
return [NSString stringWithFormat:@"HotPocket v%@ (%@)", [mainBundle.infoDictionary valueForKey:@"CFBundleShortVersionString"], [mainBundle.infoDictionary valueForKey:@"CFBundleVersion"]];
}
+(NSDictionary *)postAuthenticateURLParams {
NSDictionary *result = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"HPAuthFlowPostAuthenticateURLParts"];
if (result == nil) {
return [NSDictionary dictionary];
}
return result;
}
+(NSString *)postAuthenticateURLScheme {
NSDictionary *params = [self postAuthenticateURLParams];
return [params valueForKey:@"scheme"];
}
+(NSString *)postAuthenticateURLHost {
NSDictionary *params = [self postAuthenticateURLParams];
return [params valueForKey:@"host"];
}
@end

View File

@@ -6,12 +6,19 @@
//
#import <UIKit/UIKit.h>
#import <AuthenticationServices/AuthenticationServices.h>
NS_ASSUME_NONNULL_BEGIN
@interface AuthorizationProgressViewController : UIViewController
@class MultilineLabel;
@interface AuthorizationProgressViewController : UIViewController<ASWebAuthenticationPresentationContextProviding>
@property IBOutlet UIActivityIndicatorView *progressIndicator;
@property IBOutlet MultilineLabel *progressLabel;
@property (strong, nullable) NSURL *authorizationURL;
@property (strong, nullable) ASWebAuthenticationSession *webAuthenticationSession;
@property BOOL userCancelledSession;
@end

View File

@@ -8,20 +8,36 @@
#import "AuthorizationProgressViewController.h"
#import "AppDelegate.h"
#import "HPAuthFlow.h"
#import "HPCredentialsHelper.h"
#import "MultilineLabel.h"
#import "NSBundle+HotPocketExtensions.h"
@interface AuthorizationProgressViewController (AuthorizationProgressViewControllerPrivate)
#pragma mark - Private interface
-(void)presentAuthorizationError;
@end
@implementation AuthorizationProgressViewController
#pragma mark - View lifecycle
-(instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.authorizationURL = nil;
self.webAuthenticationSession = nil;
self.userCancelledSession = NO;
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
self.progressLabel.text = NSLocalizedString(@"Continue to sign in in your browser...", @"Continue to sign in in your browser...");
}
-(void)viewWillAppear:(BOOL)animated {
@@ -29,17 +45,90 @@
[self.progressIndicator startAnimating];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAuthFlowDidFinish:) name:@"AuthFlowDidFinish" object:appDelegate.authFlow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onAuthFlowDidFinish:)
name:@"AuthFlowDidFinish"
object:appDelegate.authFlow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onAuthFlowDidReceiveAuthParams:)
name:@"AuthFlowDidReceiveAuthParams"
object:appDelegate.authFlow];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
ASWebAuthenticationSessionCompletionHandler completionHandler = ^(NSURL *url, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error != nil) {
#ifdef DEBUG
NSLog(@"[AuthorizationViewController.session completionHandler] error=`%@`", error);
#endif
if (error.code == ASWebAuthenticationSessionErrorCodeCanceledLogin) {
self.userCancelledSession = YES;
}
[self presentAuthorizationError];
} else {
HPAuthParams *receivedAuthParams = [appDelegate.authFlow handlePostAuthenticateURL:url];
if (receivedAuthParams != nil) {
[appDelegate.authFlow handleAuthParams:receivedAuthParams];
} else {
[self presentAuthorizationError];
}
}
self.webAuthenticationSession = nil;
});
};
ASWebAuthenticationSessionCallback *callback = [ASWebAuthenticationSessionCallback callbackWithCustomScheme:[NSBundle postAuthenticateURLScheme]];
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:self.authorizationURL
callback:callback
completionHandler:completionHandler];
self.webAuthenticationSession.presentationContextProvider = self;
#ifdef DEBUG
self.webAuthenticationSession.prefersEphemeralWebBrowserSession = YES;
#endif
if (self.webAuthenticationSession.canStart == NO) {
[self presentAuthorizationError];
return;
}
[self.webAuthenticationSession start];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.webAuthenticationSession = nil;
[self.progressIndicator stopAnimating];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
# pragma mark - Private interface
-(void)presentAuthorizationError {
if (self.userCancelledSession == NO) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Oops!", @"Oops!")
message:NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.")
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Oh well", @"Oh well")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[alert dismissViewControllerAnimated:YES completion:^{
[self.navigationController popViewControllerAnimated:YES];
}];
}]];
[self presentViewController:alert animated:YES completion:nil];
} else {
[self.navigationController popViewControllerAnimated:YES];
}
}
#pragma mark - Notification handlers
@@ -52,23 +141,21 @@
HPCredentials *credentials = [[HPCredentialsHelper sharedHelper] getCredentials];
if (credentials.usable == NO) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Oops!", @"Oops!")
message:NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.")
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Oh well", @"Oh well")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[alert dismissViewControllerAnimated:YES completion:^{
[self.navigationController popViewControllerAnimated:YES];
}];
}]];
[self presentViewController:alert animated:YES completion:nil];
[self presentAuthorizationError];
} else {
[self.navigationController popToRootViewControllerAnimated:YES];
}
});
}
-(void)onAuthFlowDidReceiveAuthParams:(NSNotification *)notification {
self.progressLabel.text = NSLocalizedString(@"Processing authorization...", @"Processing authorization...");
}
# pragma mark - ASWebAuthenticationPresentationContextProviding implementation
-(ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
return self.view.window;
}
@end

View File

@@ -73,14 +73,9 @@
return;
}
if ([application canOpenURL:authURL] == YES) {
[application openURL:authURL options:@{} completionHandler:^(BOOL result) {
if (result == YES) {
AuthorizationProgressViewController *authorizationProgressViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"AuthorizationProgressViewController"];
[self.navigationController pushViewController:authorizationProgressViewController animated:YES];
}
}];
}
AuthorizationProgressViewController *authorizationProgressViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"AuthorizationProgressViewController"];
authorizationProgressViewController.authorizationURL = authURL;
[self.navigationController pushViewController:authorizationProgressViewController animated:YES];
}
#pragma mark - Event handlers

View File

@@ -229,8 +229,8 @@
<rect key="frame" x="189" y="306" width="37" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Awaiting authentication response..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qiJ-yx-nMd">
<rect key="frame" x="20" y="359" width="374" height="21"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Awaiting authentication response..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qiJ-yx-nMd" customClass="MultilineLabel">
<rect key="frame" x="20" y="359" width="374" height="64"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -242,6 +242,7 @@
</view>
<connections>
<outlet property="progressIndicator" destination="DNy-gf-n60" id="hJF-jc-ZJ0"/>
<outlet property="progressLabel" destination="qiJ-yx-nMd" id="1Wu-em-XsK"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="N3D-cM-5Ro" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>

View File

@@ -21,18 +21,6 @@
}
-(void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
HPAuthParams *receivedAuthParams = nil;
for (NSURL *url in urls) {
receivedAuthParams = [self.authFlow handlePostAuthenticateURL:url];
if (receivedAuthParams != nil) {
break;
}
}
if (receivedAuthParams != nil) {
[self.authFlow handleAuthParams:receivedAuthParams];
}
}
@end

View File

@@ -6,12 +6,19 @@
//
#import <Cocoa/Cocoa.h>
#import <AuthenticationServices/AuthenticationServices.h>
NS_ASSUME_NONNULL_BEGIN
@interface AuthorizationProgressViewController : NSViewController
@interface AuthorizationProgressViewController : NSViewController<ASWebAuthenticationPresentationContextProviding>
@property IBOutlet NSProgressIndicator *progressIndicator;
@property NSString *progressLabelTitle;
@property (nullable, strong) NSURL *authorizationURL;
@property (nullable, strong) ASWebAuthenticationSession *webAuthenticationSession;
@property BOOL userCancelledSession;
-(IBAction)doCancel:(id)sender;
@end

View File

@@ -9,37 +9,133 @@
#import "AppDelegate.h"
#import "AuthorizationViewController.h"
#import "HPAuthFlow.h"
#import "HPCredentialsHelper.h"
#import "MainViewController.h"
#import "NSBundle+HotPocketExtensions.h"
#import "ReplaceAnimator.h"
@interface AuthorizationProgressViewController (AuthorizationProgressViewControllerPrivate)
#pragma mark - Private interface
-(void)presentAuthorizationError;
@end
@implementation AuthorizationProgressViewController
#pragma mark - View lifecycle
-(instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.authorizationURL = nil;
self.webAuthenticationSession = nil;
self.userCancelledSession = NO;
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
self.progressLabelTitle = NSLocalizedString(@"Continue to sign in in your browser...", @"Continue to sign in in your browser...");
}
-(void)viewWillAppear {
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAuthFlowDidFinish:) name:@"AuthFlowDidFinish" object:appDelegate.authFlow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onAuthFlowDidFinish:)
name:@"AuthFlowDidFinish"
object:appDelegate.authFlow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onAuthFlowDidReceiveAuthParams:)
name:@"AuthFlowDidReceiveAuthParams"
object:appDelegate.authFlow];
[self.progressIndicator startAnimation:self];
}
-(void)viewDidAppear {
[super viewDidAppear];
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
ASWebAuthenticationSessionCompletionHandler completionHandler = ^(NSURL *url, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error != nil) {
#ifdef DEBUG
NSLog(@"[AuthorizationViewController.session completionHandler] error=`%@`", error);
#endif
if (error.code == ASWebAuthenticationSessionErrorCodeCanceledLogin) {
self.userCancelledSession = YES;
}
[self presentAuthorizationError];
} else {
HPAuthParams *receivedAuthParams = [appDelegate.authFlow handlePostAuthenticateURL:url];
if (receivedAuthParams != nil) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[appDelegate.authFlow handleAuthParams:receivedAuthParams];
} else {
[self presentAuthorizationError];
}
}
self.webAuthenticationSession = nil;
});
};
ASWebAuthenticationSessionCallback *callback = [ASWebAuthenticationSessionCallback callbackWithCustomScheme:[NSBundle postAuthenticateURLScheme]];
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:self.authorizationURL
callback:callback
completionHandler:completionHandler];
self.webAuthenticationSession.presentationContextProvider = self;
#ifdef DEBUG
self.webAuthenticationSession.prefersEphemeralWebBrowserSession = YES;
#endif
if (self.webAuthenticationSession.canStart == NO) {
[self presentAuthorizationError];
return;
}
[self.webAuthenticationSession start];
}
-(void)viewDidDisappear {
[super viewDidDisappear];
self.webAuthenticationSession = nil;
[self.progressIndicator stopAnimation:self];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Actions
-(IBAction)doCancel:(id)sender {
[self.webAuthenticationSession cancel];
}
#pragma mark - Private interface
-(void)presentAuthorizationError {
if (self.userCancelledSession == NO) {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleCritical;
alert.messageText = NSLocalizedString(@"Oops!", @"Oops!");
alert.informativeText = NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.");
[alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse response) {
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
}];
} else {
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
}
}
#pragma mark - Notification handlers
-(void)onAuthFlowDidFinish:(NSNotification *)notification {
@@ -50,14 +146,7 @@
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
if (credentials.usable == NO) {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleCritical;
alert.messageText = NSLocalizedString(@"Oops!", @"Oops!");
alert.informativeText = NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.");
[alert runModal];
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
[self presentAuthorizationError];
} else {
MainViewController *mainViewController = [self.storyboard instantiateControllerWithIdentifier:@"MainViewController"];
[self presentViewController:mainViewController animator:[[ReplaceAnimator alloc] init]];
@@ -65,4 +154,14 @@
});
}
-(void)onAuthFlowDidReceiveAuthParams:(NSNotification *)notification {
self.progressLabelTitle = NSLocalizedString(@"Processing authorization...", @"Processing authorization...");
}
# pragma mark - ASWebAuthenticationPresentationContextProviding implementation
-(ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
return self.view.window;
}
@end

View File

@@ -31,19 +31,24 @@
#pragma mark - Actions
-(IBAction)doStartAuthorizationFlow:(id)sender {
AppDelegate *appDeleate = [[NSApplication sharedApplication] delegate];
appDeleate.authFlow.baseURL = [NSURL URLWithString:self.baseURL];
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
appDelegate.authFlow.baseURL = [NSURL URLWithString:self.baseURL];
NSURL *authURL = [appDeleate.authFlow start];
NSURL *authURL = [appDelegate.authFlow start];
if (authURL == nil) {
NSBeep();
return;
}
AuthorizationProgressViewController *authProgressViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationProgressViewController"];
[self presentViewController:authProgressViewController animator:[[ReplaceAnimator alloc] init]];
[[NSWorkspace sharedWorkspace] openURL:authURL];
AuthorizationProgressViewController *authProgressViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationProgressViewController"];
authProgressViewController.authorizationURL = authURL;
[self presentViewController:authProgressViewController animator:[[ReplaceAnimator alloc] init]];
}
# pragma mark - ASWebAuthenticationPresentationContextProviding implementation
-(ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
return self.view.window;
}
@end

View File

@@ -103,7 +103,7 @@
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7sM-F3-Zzf">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7sM-F3-Zzf">
<rect key="frame" x="18" y="153" width="389" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="HotPocket Instance URL" id="XwM-DV-kei">
@@ -112,7 +112,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ygC-xe-m6y">
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ygC-xe-m6y">
<rect key="frame" x="20" y="124" width="385" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="rHK-hP-yWO">
@@ -128,7 +128,7 @@
</binding>
</connections>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DIc-8O-uoQ">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DIc-8O-uoQ">
<rect key="frame" x="18" y="68" width="389" height="48"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" title="Enter the URL to your HotPocket instance, e.g. https://hotpocket.yourcompany.com/" id="Y0q-a1-oBP">
@@ -154,7 +154,7 @@
<action selector="doStartAuthorizationFlow:" target="XfG-lQ-9wD" id="AOi-Wt-gmL"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mQc-Ea-NNN">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mQc-Ea-NNN">
<rect key="frame" x="18" y="185" width="389" height="28"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="NTZ-zl-yhk">
@@ -177,7 +177,7 @@
<rect key="frame" x="0.0" y="0.0" width="425" height="325"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yRj-hC-QYS">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yRj-hC-QYS">
<rect key="frame" x="18" y="185" width="389" height="28"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="F4l-2Z-D79">
@@ -195,15 +195,32 @@
<rect key="frame" x="196" y="113" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g9a-gR-c7o">
<rect key="frame" x="18" y="81" width="389" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g9a-gR-c7o">
<rect key="frame" x="18" y="49" width="389" height="48"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Awaiting authorization response..." id="3oi-LK-vKv">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="OX4-Oj-1cw" name="value" keyPath="self.progressLabelTitle" id="ydU-jy-p3F"/>
</connections>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Sip-bU-V5i">
<rect key="frame" x="175" y="14" width="76" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nYJ-LO-V7O">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="doCancel:" target="OX4-Oj-1cw" id="nAC-If-aib"/>
</connections>
</button>
</subviews>
</view>
<connections>
@@ -227,7 +244,7 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="icon-mac-384" id="fae-mz-0sj"/>
</imageView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-KB-3Ut">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-KB-3Ut">
<rect key="frame" x="18" y="185" width="389" height="28"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="r5O-Sk-IdK">
@@ -236,7 +253,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2h7-bN-dsa">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2h7-bN-dsa">
<rect key="frame" x="18" y="153" width="389" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" title="HotPocket is configured and ready." id="5fh-mh-WR1">
@@ -245,7 +262,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uci-UC-wxo">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uci-UC-wxo">
<rect key="frame" x="18" y="89" width="389" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Instance URL" id="azk-ea-KeN">
@@ -267,8 +284,8 @@
<binding destination="r5D-xE-cNT" name="enabled" keyPath="self.logoutButtonEnabled" id="gTs-BO-USz"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8H3-oU-acU" customClass="LinkLabel">
<rect key="frame" x="18" y="65" width="389" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8H3-oU-acU" customClass="LinkLabel">
<rect key="frame" x="18" y="69" width="389" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" allowsEditingTextAttributes="YES" id="EoA-mM-phM">
<font key="font" metaFont="system"/>
@@ -276,7 +293,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9pl-Ap-yxc">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9pl-Ap-yxc">
<rect key="frame" x="18" y="113" width="389" height="32"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" title="Safari and Share Extensions are installed." id="dy7-bw-DYh">

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "hotpocket-apple"
version = "25.11.19"
version = "26.3.16"
description = "HotPocket Apple Integrations"
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
license = "Apache-2.0"

View File

@@ -27,7 +27,7 @@ COPY --chown=$APP_USER_UID:$APP_USER_GID packages/common/ /srv/packages/common/
COPY --chown=$APP_USER_UID:$APP_USER_GID packages/soa/ /srv/packages/soa/
RUN poetry install --only main,deployment && \
minify -i --css-precision 0 --js-precision 0 --js-version 2022 hotpocket_backend/apps/ui/static/ui/css/hotpocket-backend*.css hotpocket_backend/apps/ui/static/ui/js/hotpocket*.js && \
minify -i --css-precision 0 --js-precision 0 --js-version 2022 hotpocket_backend/apps/ui/static/ui/css/hotpocket-backend*.css hotpocket_backend/apps/ui/static/ui/js/hotpocket-backend*.js && \
./manage.py collectstatic --settings hotpocket_backend.settings.deployment.build --noinput && \
find hotpocket_backend/static/ -name "*.map*" -delete && \
rm -f hotpocket_backend/settings/deployment/build.py && \

View File

@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
version = '25.11.19'
version = '26.3.16'

View File

@@ -1,2 +1,3 @@
from .access_token import AccessToken # noqa: F401
from .account import AccountAdmin # noqa: F401
from .auth_key import AuthKey # noqa: F401

View File

@@ -2,16 +2,41 @@
from __future__ import annotations
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from hotpocket_backend.apps.accounts.models import AccessToken
class AccessTokenAdmin(admin.ModelAdmin):
list_display = ('pk', 'account_uuid', 'origin', 'created_at', 'is_active')
list_display = (
'pk', 'account_uuid', 'origin', 'created_at', 'render_is_active',
)
search_fields = ('pk', 'account_uuid', 'key', 'origin')
readonly_fields = (
'pk',
'account_uuid',
'key',
'origin',
'meta',
'created_at',
'deleted_at',
)
ordering = ['-created_at']
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
@admin.display(
description=_('Is Active?'), boolean=True, ordering='-deleted_at',
)
def render_is_active(self, obj: AccessToken | None = None) -> bool | None:
if obj is None:
return None
return obj.is_active
admin.site.register(AccessToken, AccessTokenAdmin)

View File

@@ -3,15 +3,26 @@ from __future__ import annotations
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from hotpocket_backend.apps.accounts.models import Account
class AccountAdmin(UserAdmin):
list_display = (*UserAdmin.list_display, 'is_active')
list_display = (*UserAdmin.list_display, 'render_is_active')
ordering = ['username']
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
@admin.display(
description=_('Is Active?'), boolean=True, ordering='-is_active',
)
def render_is_active(self, obj: Account | None = None) -> bool | None:
if obj is None:
return None
return obj.is_active
admin.site.register(Account, AccountAdmin)

View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from hotpocket_backend.apps.accounts.models import AuthKey
class AuthKeyAdmin(admin.ModelAdmin):
list_display = (
'pk', 'account_uuid', 'key', 'created_at', 'consumed_at', 'render_is_active',
)
search_fields = ('pk', 'account_uuid', 'key')
readonly_fields = (
'pk',
'account_uuid',
'key',
'consumed_at',
'created_at',
'deleted_at',
)
ordering = ['-created_at']
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
@admin.display(
description=_('Is Active?'), boolean=True, ordering='-deleted_at',
)
def render_is_active(self, obj: AuthKey | None = None) -> bool | None:
if obj is None:
return None
return obj.is_active
admin.site.register(AuthKey, AuthKeyAdmin)

View File

@@ -38,3 +38,5 @@ class PSettings(typing.Protocol):
UI_PAGE_HEAD_INCLUDES: list
UI_PAGE_SCRIPT_INCLUDES: list
OPERATOR_EMAIL: str | None

View File

@@ -1 +1,2 @@
from . import association # noqa: F401
from . import save # noqa: F401

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from hotpocket_backend.apps.saves.models import Association
class AssociationAdmin(admin.ModelAdmin):
list_display = (
'pk', 'account_uuid', 'target', 'created_at', 'render_is_active',
)
search_fields = ('pk', 'account_uuid')
fields = (
'pk',
'account_uuid',
'archived_at',
'starred_at',
'target_meta',
'target_title',
'target_description',
'created_at',
'deleted_at',
)
readonly_fields = (
'pk',
'account_uuid',
'target_meta',
'target',
'archived_at',
'starred_at',
'created_at',
'deleted_at',
)
ordering = ['-created_at']
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
@admin.display(
description=_('Is Active?'), boolean=True, ordering='-deleted_at',
)
def render_is_active(self, obj: Association | None = None) -> bool | None:
if obj is None:
return None
return obj.is_active
admin.site.register(Association, AssociationAdmin)

View File

@@ -11,6 +11,26 @@ class SaveAdmin(admin.ModelAdmin):
list_display = (
'pk', 'key', 'account_uuid', 'created_at', 'render_is_active',
)
search_fields = ('pk', 'account_uuid', 'url')
fields = (
'pk',
'account_uuid',
'key',
'url',
'title',
'description',
'last_processed_at',
'is_netloc_banned',
'created_at',
'deleted_at',
)
readonly_fields = (
'pk',
'account_uuid',
'key',
'created_at',
'deleted_at',
)
ordering = ['-created_at']
def has_delete_permission(self, request, obj=None):

View File

@@ -100,3 +100,9 @@ def page_includes(request: HttpRequest) -> dict:
'script': settings.UI_PAGE_SCRIPT_INCLUDES,
},
}
def operator_email(request: HttpRequest) -> dict:
return {
'OPERATOR_EMAIL': settings.OPERATOR_EMAIL,
}

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import uuid
import pydantic
class SavesCreateOut(pydantic.BaseModel):
id: uuid.UUID
target_uuid: uuid.UUID
url: pydantic.AnyHttpUrl
def to_rpc(self) -> dict:
return {
'id': self.id,
'target_uuid': self.target_uuid,
'url': str(self.url),
}

View File

@@ -151,7 +151,6 @@ class SettingsForm(Form):
('cosmo', _('Cosmo')),
('solar', _('Solar')),
('sandstone', _('Sandstone')),
('sketchy', _('Sketchy')),
],
)
light_mode = forms.BooleanField(

View File

@@ -3,19 +3,28 @@ from __future__ import annotations
from bthlabs_jsonrpc_core import register_method
from django.http import HttpRequest
from django.urls import reverse
from hotpocket_backend.apps.core.rpc import wrap_soa_errors
from hotpocket_backend.apps.ui.dto.rpc import SavesCreateOut
from hotpocket_backend.apps.ui.services.workflows import CreateSaveWorkflow
from hotpocket_soa.dto.associations import AssociationOut
@register_method(method='saves.create')
@wrap_soa_errors
def create(request: HttpRequest, url: str) -> AssociationOut:
def create(request: HttpRequest, url: str) -> SavesCreateOut:
association = CreateSaveWorkflow().run_rpc(
request=request,
account=request.user,
url=url,
)
return association
result = SavesCreateOut.model_validate({
'id': association.pk,
'target_uuid': association.target_uuid,
'url': request.build_absolute_uri(reverse(
'ui.associations.view', args=(association.pk,),
)),
})
return result

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -45,7 +45,6 @@
return null;
}
onLoad = (event) => {
console.log('HotPocketApp.onLoad()', event);
for (let pluginSpec of this.plugins) {
if (pluginSpec[1].onLoad) {
pluginSpec[1].onLoad(event);

View File

@@ -36,6 +36,12 @@
{% blocktranslate %}Log in with {{ HOTPOCKET_OIDC_DISPLAY_NAME }}{% endblocktranslate %}
</a>
{% endif %}
{% if OPERATOR_EMAIL %}
<p class="my-0 mt-2 text-center">
<small>{% blocktranslate %}For support with your account, contact the <a href="mailto:{{ OPERATOR_EMAIL }}">instance operator</a>.{% endblocktranslate %}</small>
</p>
{% endif %}
</div>
</div>
{% include "ui/ui/partials/uname.html" %}

View File

@@ -16,7 +16,7 @@
{% endif %}
</div>
<div class="card-footer d-flex align-items-center">
<a href="{{ association.target.url }}" target="_blank" rel="noopener noreferrer"><small>{{ association.target.url|render_url_domain }}</small></a>
<a href="{{ association.target.url }}" target="_blank" rel="noopener noreferrer"><small>{{ association.target.url|render_url_domain }} <i class="bi bi-box-arrow-up-right"></i></small></a>
<div class="ms-auto flex-shrink-0 d-flex align-items-center">
{% if not association.archived_at %}
<div class="spinner-border spinner-border-sm ui-htmx-indicator" role="status">

View File

@@ -2,10 +2,24 @@
{% load i18n static ui %}
{% block title %}{{ association.title }}{% endblock %}
{% block title %}{% if association.title %}{{ association.title }}{% else %}{{ association.target.url }}{% endif %}{% endblock %}
{% block button_bar_class %}d-none{% endblock %}
{% block page_head %}
{{ block.super}}
<meta property="og:title" content="{% if association.title %}{{ association.title }}{% else %}{{ association.target.url }}{% endif %}">
{% if association.description %}
<meta property="og:description" content="{{ association.description|truncatechars:125 }}">
{% endif %}
<meta property="og:image" content="{{ og_card_url }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:url" content="{{ share_url }}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{ SITE_TITLE }}">
{% endblock %}
{% block page_body %}
<div id="ViewAssociationView" class="container pb-3">
<p class="display-3 mt-3 mb-0 text-center">

View File

@@ -130,6 +130,56 @@
{% translate 'Log out' %}
</a>
</li>
<li class="nav-item px-3 d-flex justify-content-center">
{% spaceless %}
<a
class="btn btn-outline-info btn-sm"
href="https://apps.apple.com/pl/app/hotpocket-by-bthlabs/id6752321380"
rel="noopener noreferrer"
target="_blank"
title="{% translate 'Safari and Share extension for iOS and macOS' %}"
>
<i class="bi bi-apple"></i>
</a>
<a
class="btn btn-outline-info btn-sm ms-1"
href="https://chromewebstore.google.com/detail/save-to-hotpocket/mkmoejhhgnadmijpgkkioicjmikkkjbd"
rel="noopener noreferrer"
target="_blank"
title="{% translate 'Chrome extension' %}"
>
<i class="bi bi-browser-chrome"></i>
</a>
<a
class="btn btn-outline-info btn-sm ms-1"
href="https://addons.mozilla.org/en-GB/firefox/addon/save-to-hotpocket/"
rel="noopener noreferrer"
target="_blank"
title="{% translate 'Firefox extension' %}"
>
<i class="bi bi-browser-firefox"></i>
</a>
<a
class="btn btn-outline-info btn-sm ms-1"
data-bs-toggle="modal"
data-bs-target="#modalPWA"
href="#"
title="{% translate 'Android PWA' %}"
>
<i class="bi bi-android2"></i>
</a>
<div class="vr my-1 ms-1 opacity-75"></div>
<a
class="btn btn-outline-info btn-sm ms-1"
href="https://git.bthlabs.pl/tomekwojcik/hotpocket"
rel="noopener noreferrer"
target="_blank"
title="{% translate 'Source code repository' %}"
>
<i class="bi bi-git"></i>
</a>
{% endspaceless %}
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'ui.accounts.login' %}">
@@ -142,6 +192,38 @@
{% include "ui/ui/partials/uname.html" %}
</div>
</div>
{% if not request.user.is_anonymous %}
<div class="modal modal-fade" id="modalPWA" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="modalPWALabel">
{% translate 'HotPocket on Android and others' %}
</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="mb-1">
{% blocktranslate %}
HotPocket doesn't natively support Android and other systems. However, it's a Progressive Web Application. You can install it from your browser and it'll register itself as share target.
{% endblocktranslate %}
</p>
<p class="mb-0">
{% blocktranslate %}
This is currently supported on Android and Windows 11 (when installed using Edge).
{% endblocktranslate %}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">
{% translate 'Cool' %}
</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block scripts %}

View File

@@ -3,11 +3,13 @@
{% if save.is_youtube_video %}
<div class="mb-0 d-flex justify-content-center">
<iframe
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allow="accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;"
allowfullscreen
class="ui-youtube-iframe"
frameborder="0"
height="200"
referrerpolicy="strict-origin"
scrolling="no"
src="{{ save|render_youtube_embed_url }}"
title="YouTube video player"
width="320"

View File

@@ -5,9 +5,11 @@ import logging
import uuid
from django.contrib import messages
from django.contrib.auth.views import redirect_to_login
import django.db
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import redirect, render
from django.templatetags.static import static
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import FormView, View
@@ -25,7 +27,7 @@ from hotpocket_backend.apps.ui.forms.associations import (
RefreshForm,
)
from hotpocket_backend.apps.ui.services import UIAssociationsService
from hotpocket_common.constants import NULL_UUID, AssociationsSearchMode
from hotpocket_common.constants import AssociationsSearchMode
from hotpocket_soa.dto.associations import (
AssociationOut,
AssociationsQuery,
@@ -175,7 +177,9 @@ def view(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
if is_share is True:
account_uuid = None
else:
account_uuid = NULL_UUID
return redirect_to_login(
reverse('ui.associations.view', args=(pk,)),
)
else:
if is_share is False:
account_uuid = request.user.pk
@@ -198,12 +202,18 @@ def view(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
if is_share is True:
show_controls = show_controls and False
share_url = reverse(
'ui.associations.view',
args=(association.pk,),
query=[
('share', 'true'),
],
share_url = request.build_absolute_uri(
reverse(
'ui.associations.view',
args=(association.pk,),
query=[
('share', 'true'),
],
),
)
og_card_url = request.build_absolute_uri(
static('ui/img/og-card.png'),
)
return render(
@@ -213,6 +223,12 @@ def view(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
'association': association,
'show_controls': show_controls,
'share_url': share_url,
'is_share': is_share,
'og_card_url': (
og_card_url
if is_share is True
else None
),
},
)

View File

@@ -27,9 +27,6 @@ def page_not_found(request: HttpRequest,
exception: Exception,
template_name: str = ERROR_404_TEMPLATE_NAME,
) -> HttpResponseNotFound:
if exception:
LOGGER.error('Exception: %s', exception, exc_info=exception)
return HttpResponseNotFound(render_to_string(
'ui/errors/page_not_found.html',
context={},

View File

@@ -72,6 +72,7 @@ TEMPLATES = [
'hotpocket_backend.apps.ui.context_processors.debug',
'hotpocket_backend.apps.ui.context_processors.version',
'hotpocket_backend.apps.ui.context_processors.appearance_settings',
'hotpocket_backend.apps.ui.context_processors.operator_email',
],
},
},
@@ -295,3 +296,5 @@ SAVES_ASSOCIATION_ADAPTER = os.environ.get(
UPLOADS_PATH = None
SITE_SHORT_TITLE = 'HotPocket'
OPERATOR_EMAIL = os.environ.get('HOTPOCKET_BACKEND_OPERATOR_EMAIL', None)

View File

@@ -13,7 +13,7 @@ cat <<EOF
|_|
production
HotPocket v25.11.19 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/)
HotPocket v26.3.16 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/)
Copyright 2025-present by BTHLabs. All rights reserved. (https://bthlabs.pl/)
Licensed under Apache-2.0
EOF

View File

@@ -1,6 +1,6 @@
{
"name": "hotpocket-backend",
"version": "25.11.19",
"version": "26.3.16",
"description": "HotPocket Backend",
"main": "hotpocket_backend/apps/frontend/src/index.js",
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",

View File

@@ -148,14 +148,14 @@ django = ">=4.2,<5.3"
[[package]]
name = "celery"
version = "5.5.3"
version = "5.6.2"
description = "Distributed Task Queue."
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "celery-5.5.3-py3-none-any.whl", hash = "sha256:0b5761a07057acee94694464ca482416b959568904c9dfa41ce8413a7d65d525"},
{file = "celery-5.5.3.tar.gz", hash = "sha256:6c972ae7968c2b5281227f01c3a3f984037d21c5129d07bf3550cc2afc6b10a5"},
{file = "celery-5.6.2-py3-none-any.whl", hash = "sha256:3ffafacbe056951b629c7abcf9064c4a2366de0bdfc9fdba421b97ebb68619a5"},
{file = "celery-5.6.2.tar.gz", hash = "sha256:4a8921c3fcf2ad76317d3b29020772103581ed2454c4c042cc55dcc43585009b"},
]
[package.dependencies]
@@ -164,13 +164,14 @@ click = ">=8.1.2,<9.0"
click-didyoumean = ">=0.3.0"
click-plugins = ">=1.1.1"
click-repl = ">=0.2.0"
kombu = ">=5.5.2,<5.6"
kombu = ">=5.6.0"
python-dateutil = ">=2.8.2"
tzlocal = "*"
vine = ">=5.1.0,<6.0"
[package.extras]
arangodb = ["pyArango (>=2.0.2)"]
auth = ["cryptography (==44.0.2)"]
auth = ["cryptography (==46.0.3)"]
azureblockblob = ["azure-identity (>=1.19.0)", "azure-storage-blob (>=12.15.0)"]
brotli = ["brotli (>=1.0.0) ; platform_python_implementation == \"CPython\"", "brotlipy (>=0.7.0) ; platform_python_implementation == \"PyPy\""]
cassandra = ["cassandra-driver (>=3.25.0,<4)"]
@@ -180,15 +181,15 @@ couchbase = ["couchbase (>=3.0.0) ; platform_python_implementation != \"PyPy\" a
couchdb = ["pycouchdb (==1.16.0)"]
django = ["Django (>=2.2.28)"]
dynamodb = ["boto3 (>=1.26.143)"]
elasticsearch = ["elastic-transport (<=8.17.1)", "elasticsearch (<=8.17.2)"]
elasticsearch = ["elastic-transport (<=9.1.0)", "elasticsearch (<=9.1.2)"]
eventlet = ["eventlet (>=0.32.0) ; python_version < \"3.10\""]
gcs = ["google-cloud-firestore (==2.20.1)", "google-cloud-storage (>=2.10.0)", "grpcio (==1.67.0)"]
gcs = ["google-cloud-firestore (==2.22.0)", "google-cloud-storage (>=2.10.0)", "grpcio (==1.75.1)"]
gevent = ["gevent (>=1.5.0)"]
librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""]
memcache = ["pylibmc (==1.6.3) ; platform_system != \"Windows\""]
mongodb = ["kombu[mongodb]"]
msgpack = ["kombu[msgpack]"]
pydantic = ["pydantic (>=2.4)"]
pydantic = ["pydantic (>=2.12.0a1) ; python_version >= \"3.14\"", "pydantic (>=2.4) ; python_version < \"3.14\""]
pymemcache = ["python-memcached (>=1.61)"]
pyro = ["pyro4 (==4.82) ; python_version < \"3.11\""]
pytest = ["pytest-celery[all] (>=1.2.0,<1.3.0)"]
@@ -197,8 +198,8 @@ s3 = ["boto3 (>=1.26.143)"]
slmq = ["softlayer_messaging (>=1.0.3)"]
solar = ["ephem (==4.2) ; platform_python_implementation != \"PyPy\""]
sqlalchemy = ["kombu[sqlalchemy]"]
sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.5.0)", "urllib3 (>=1.26.16)"]
tblib = ["tblib (>=1.3.0) ; python_version < \"3.8.0\"", "tblib (>=1.5.0) ; python_version >= \"3.8.0\""]
sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.5.0)", "pycurl (>=7.43.0.5,<7.45.4) ; sys_platform != \"win32\" and platform_python_implementation == \"CPython\" and python_version < \"3.9\"", "pycurl (>=7.45.4) ; sys_platform != \"win32\" and platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "urllib3 (>=1.26.16)"]
tblib = ["tblib (==3.2.2)"]
yaml = ["kombu[yaml]"]
zookeeper = ["kazoo (>=1.3.1)"]
zstd = ["zstandard (==0.23.0)"]
@@ -518,23 +519,20 @@ markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win
[[package]]
name = "crispy-bootstrap5"
version = "2025.6"
version = "2026.3"
description = "Bootstrap5 template pack for django-crispy-forms"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "crispy_bootstrap5-2025.6-py3-none-any.whl", hash = "sha256:a343aa128b4383f35f00295b94de2b10862f2a4f24eda21fa6ead45234c07050"},
{file = "crispy_bootstrap5-2025.6.tar.gz", hash = "sha256:f1bde7cac074c650fc82f31777d4a4cfd0df2512c68bc4128f259c75d3daada4"},
{file = "crispy_bootstrap5-2026.3-py3-none-any.whl", hash = "sha256:e0fff85c0503e9aed610a0ee31368e2191d340657f813669491c288c1c2e2dfa"},
{file = "crispy_bootstrap5-2026.3.tar.gz", hash = "sha256:e7f5adb36acfbb456444c46e82c436931c796c539e9c620be4fa9dc9c9d6679c"},
]
[package.dependencies]
django = ">=4.2"
django-crispy-forms = ">=2.3"
[package.extras]
test = ["pytest", "pytest-django"]
[[package]]
name = "cryptography"
version = "46.0.2"
@@ -650,14 +648,14 @@ files = [
[[package]]
name = "django"
version = "5.2.8"
version = "5.2.12"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
groups = ["main", "dev"]
files = [
{file = "django-5.2.8-py3-none-any.whl", hash = "sha256:37e687f7bd73ddf043e2b6b97cfe02fcbb11f2dbb3adccc6a2b18c6daa054d7f"},
{file = "django-5.2.8.tar.gz", hash = "sha256:23254866a5bb9a2cfa6004e8b809ec6246eba4b58a7589bc2772f1bcc8456c7f"},
{file = "django-5.2.12-py3-none-any.whl", hash = "sha256:4853482f395c3a151937f6991272540fcbf531464f254a347bf7c89f53c8cff7"},
{file = "django-5.2.12.tar.gz", hash = "sha256:6b809af7165c73eff5ce1c87fdae75d4da6520d6667f86401ecf55b681eb1eeb"},
]
[package.dependencies]
@@ -687,18 +685,18 @@ django = ">=4.2"
[[package]]
name = "django-crispy-forms"
version = "2.5"
version = "2.6"
description = "Best way to have Django DRY forms"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "django_crispy_forms-2.5-py3-none-any.whl", hash = "sha256:adc99d5901baca09479c53bf536b3909e80a9f2bb299438a223de4c106ebf1f9"},
{file = "django_crispy_forms-2.5.tar.gz", hash = "sha256:066e72a8f152a1334f1c811cc37740868efe3265e5a218f79079ef89f848c3d8"},
{file = "django_crispy_forms-2.6-py3-none-any.whl", hash = "sha256:8ee0ae28b6b0ac41ff48a65944480c049fe8d1b0047086874fd7efabf4ec1374"},
{file = "django_crispy_forms-2.6.tar.gz", hash = "sha256:4921a1087c6cd4f9fa3c139654c1de1c1c385f8bd6729aaee530bc0121ab4b93"},
]
[package.dependencies]
django = ">=4.2"
django = ">=5.2"
[[package]]
name = "django-extensions"
@@ -717,14 +715,14 @@ django = ">=4.2"
[[package]]
name = "django-htmx"
version = "1.26.0"
version = "1.27.0"
description = "Extensions for using Django with htmx."
optional = false
python-versions = ">=3.9"
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "django_htmx-1.26.0-py3-none-any.whl", hash = "sha256:3a80ffaa6df5a07e833752cd8ada7cee0fa711787841ab17a805075b1aecacc7"},
{file = "django_htmx-1.26.0.tar.gz", hash = "sha256:88ecc2f8a3f13ad5a50e6b16be127f04fba369124cc40a09b21ce33babb04aa6"},
{file = "django_htmx-1.27.0-py3-none-any.whl", hash = "sha256:13e1e13b87d39b57f95aae6e4987cb3df056d0b1373a41f4a94504a00298ffd8"},
{file = "django_htmx-1.27.0.tar.gz", hash = "sha256:036e5da801bfdf5f1ca815f21592cfb9f004a898f330c842f15e55c70e301a75"},
]
[package.dependencies]
@@ -945,7 +943,7 @@ files = []
develop = true
[package.dependencies]
pydantic = "2.12.4"
pydantic = "2.12.5"
[package.source]
type = "directory"
@@ -962,7 +960,7 @@ files = []
develop = true
[package.dependencies]
django = "5.2.8"
django = "5.2.12"
uuid6 = "2025.0.1"
[package.source]
@@ -980,7 +978,7 @@ files = []
develop = true
[package.dependencies]
pydantic = "2.12.4"
pydantic = "2.12.5"
[package.source]
type = "directory"
@@ -1215,20 +1213,20 @@ reference = "nexus"
[[package]]
name = "kombu"
version = "5.5.4"
version = "5.6.2"
description = "Messaging library for Python."
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "kombu-5.5.4-py3-none-any.whl", hash = "sha256:a12ed0557c238897d8e518f1d1fdf84bd1516c5e305af2dacd85c2015115feb8"},
{file = "kombu-5.5.4.tar.gz", hash = "sha256:886600168275ebeada93b888e831352fe578168342f0d1d5833d88ba0d847363"},
{file = "kombu-5.6.2-py3-none-any.whl", hash = "sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93"},
{file = "kombu-5.6.2.tar.gz", hash = "sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55"},
]
[package.dependencies]
amqp = ">=5.1.1,<6.0.0"
packaging = "*"
tzdata = {version = ">=2025.2", markers = "python_version >= \"3.9\""}
tzdata = ">=2025.2"
vine = "5.1.0"
[package.extras]
@@ -1236,16 +1234,16 @@ azureservicebus = ["azure-servicebus (>=7.10.0)"]
azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"]
confluentkafka = ["confluent-kafka (>=2.2.0)"]
consul = ["python-consul2 (==0.1.5)"]
gcpubsub = ["google-cloud-monitoring (>=2.16.0)", "google-cloud-pubsub (>=2.18.4)", "grpcio (==1.67.0)", "protobuf (==4.25.5)"]
gcpubsub = ["google-cloud-monitoring (>=2.16.0)", "google-cloud-pubsub (>=2.18.4)", "grpcio (==1.75.1)", "protobuf (==6.32.1)"]
librabbitmq = ["librabbitmq (>=2.0.0) ; python_version < \"3.11\""]
mongodb = ["pymongo (==4.10.1)"]
msgpack = ["msgpack (==1.1.0)"]
mongodb = ["pymongo (==4.15.3)"]
msgpack = ["msgpack (==1.1.2)"]
pyro = ["pyro4 (==4.82)"]
qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"]
redis = ["redis (>=4.5.2,!=4.5.5,!=5.0.2,<=5.2.1)"]
qpid = ["qpid-python (==1.36.0-1)", "qpid-tools (==1.36.0-1)"]
redis = ["redis (>=4.5.2,!=4.5.5,!=5.0.2,<6.5)"]
slmq = ["softlayer_messaging (>=1.0.3)"]
sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"]
sqs = ["boto3 (>=1.26.143)", "urllib3 (>=1.26.16)"]
sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5) ; sys_platform != \"win32\" and platform_python_implementation == \"CPython\"", "urllib3 (>=1.26.16)"]
yaml = ["PyYAML (>=3.10)"]
zookeeper = ["kazoo (>=2.8.0)"]
@@ -1610,99 +1608,92 @@ wcwidth = "*"
[[package]]
name = "psycopg"
version = "3.2.12"
version = "3.3.3"
description = "PostgreSQL database adapter for Python"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "psycopg-3.2.12-py3-none-any.whl", hash = "sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee"},
{file = "psycopg-3.2.12.tar.gz", hash = "sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b"},
{file = "psycopg-3.3.3-py3-none-any.whl", hash = "sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698"},
{file = "psycopg-3.3.3.tar.gz", hash = "sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9"},
]
[package.dependencies]
psycopg-binary = {version = "3.2.12", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""}
psycopg-binary = {version = "3.3.3", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""}
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
binary = ["psycopg-binary (==3.2.12) ; implementation_name != \"pypy\""]
c = ["psycopg-c (==3.2.12) ; implementation_name != \"pypy\""]
dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"]
binary = ["psycopg-binary (==3.3.3) ; implementation_name != \"pypy\""]
c = ["psycopg-c (==3.3.3) ; implementation_name != \"pypy\""]
dev = ["ast-comments (>=1.1.2)", "black (>=26.1.0)", "codespell (>=2.2)", "cython-lint (>=0.16)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.19.0)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"]
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
pool = ["psycopg-pool"]
test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
test = ["anyio (>=4.0)", "mypy (>=1.19.0) ; implementation_name != \"pypy\"", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
[[package]]
name = "psycopg-binary"
version = "3.2.12"
version = "3.3.3"
description = "PostgreSQL database adapter for Python -- C optimisation distribution"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.10"
groups = ["main"]
markers = "implementation_name != \"pypy\""
files = [
{file = "psycopg_binary-3.2.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13cd057f406d2c8063ae8b489395b089a7f23c39aff223b5ea39f0c4dd640550"},
{file = "psycopg_binary-3.2.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef92d5ba6213de060d1390b1f71f5c3b2fbb00b4d55edee39f3b07234538b64a"},
{file = "psycopg_binary-3.2.12-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:95f2806097a49bfd57e0c6a178f77b99487c53c157d9d507aee9c40dd58efdb4"},
{file = "psycopg_binary-3.2.12-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:ce68839da386f137bc8d814fdbeede8f89916b8605e3593a85b504a859243af9"},
{file = "psycopg_binary-3.2.12-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:940ac69ef6e89c17b3d30f3297a2ad03efdd06a4b1857f81bc533a9108a90eb9"},
{file = "psycopg_binary-3.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:310c95a68a9b948b89d6d187622757d57b6c26cece3c3f7c2cbb645ee36531b2"},
{file = "psycopg_binary-3.2.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f7c81bc60560be9eb3c23601237765069ebfa9881097ce19ca6b5ea17c5faa8f"},
{file = "psycopg_binary-3.2.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1c1dbeb8e97d00a33dfa9987776ce3d1c1e4cc251dfbd663b8f9e173f5c89d17"},
{file = "psycopg_binary-3.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:8335d989a4e94df2ccd8a1acbba9d03c4157ea8d73b65b79d447c6dc10b001d8"},
{file = "psycopg_binary-3.2.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16db2549a31ccd4887bef05570d95036813ce25fd9810b523ba1c16b0f6cfd90"},
{file = "psycopg_binary-3.2.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b9a99ded7d19b24d3b6fa632b58e52bbdecde7e1f866c3b23d0c27b092af4e3"},
{file = "psycopg_binary-3.2.12-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:385c7b5cfffac115f413b8e32c941c85ea0960e0b94a6ef43bb260f774c54893"},
{file = "psycopg_binary-3.2.12-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9c674887d1e0d4384c06c822bc7fcfede4952742e232ec1e76b5a6ae39a3ddd4"},
{file = "psycopg_binary-3.2.12-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72fd979e410ba7805462817ef8ed6f37dd75f9f4ae109bdb8503e013ccecb80b"},
{file = "psycopg_binary-3.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82fa5134517af44e28a30c38f34384773a0422ffd545fd298433ea9f2cc5a9"},
{file = "psycopg_binary-3.2.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:100fdfee763d701f6da694bde711e264aca4c2bc84fb81e1669fb491ce11d219"},
{file = "psycopg_binary-3.2.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:802bd01fb18a0acb0dea491f69a9a2da6034f33329a62876ab5b558a1fb66b45"},
{file = "psycopg_binary-3.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:f33c9e12ed05e579b7fb3c8fdb10a165f41459394b8eb113e7c377b2bd027f61"},
{file = "psycopg_binary-3.2.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ea9751310b840186379c949ede5a5129b31439acdb929f3003a8685372117ed8"},
{file = "psycopg_binary-3.2.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9fdf3a0c24822401c60c93640da69b3dfd4d9f29c3a8d797244fe22bfe592823"},
{file = "psycopg_binary-3.2.12-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49582c3b6d578bdaab2932b59f70b1bd93351ed4d594b2c97cea1611633c9de1"},
{file = "psycopg_binary-3.2.12-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5b6e505618cb376a7a7d6af86833a8f289833fe4cc97541d7100745081dc31bd"},
{file = "psycopg_binary-3.2.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6a898717ab560db393355c6ecf39b8c534f252afc3131480db1251e061090d3a"},
{file = "psycopg_binary-3.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bfd632f7038c76b0921f6d5621f5ba9ecabfad3042fa40e5875db11771d2a5de"},
{file = "psycopg_binary-3.2.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3e9c9e64fb7cda688e9488402611c0be2c81083664117edcc709d15f37faa30f"},
{file = "psycopg_binary-3.2.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c1e38b1eda54910628f68448598139a9818973755abf77950057372c1fe89a6"},
{file = "psycopg_binary-3.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:77690f0bf08356ca00fc357f50a5980c7a25f076c2c1f37d9d775a278234fefd"},
{file = "psycopg_binary-3.2.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:442f20153415f374ae5753ca618637611a41a3c58c56d16ce55f845d76a3cf7b"},
{file = "psycopg_binary-3.2.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:79de3cc5adbf51677009a8fda35ac9e9e3686d5595ab4b0c43ec7099ece6aeb5"},
{file = "psycopg_binary-3.2.12-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:095ccda59042a1239ac2fefe693a336cb5cecf8944a8d9e98b07f07e94e2b78d"},
{file = "psycopg_binary-3.2.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:efab679a2c7d1bf7d0ec0e1ecb47fe764945eff75bb4321f2e699b30a12db9b3"},
{file = "psycopg_binary-3.2.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d369e79ad9647fc8217cbb51bbbf11f9a1ffca450be31d005340157ffe8e91b3"},
{file = "psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eedc410f82007038030650aa58f620f9fe0009b9d6b04c3dc71cbd3bae5b2675"},
{file = "psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bae4be7f6781bf6c9576eedcd5e1bb74468126fa6de991e47cdb1a8ea3a42a"},
{file = "psycopg_binary-3.2.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8ffe75fe6be902dadd439adf4228c98138a992088e073ede6dd34e7235f4e03e"},
{file = "psycopg_binary-3.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:2598d0e4f2f258da13df0560187b3f1dfc9b8688c46b9d90176360ae5212c3fc"},
{file = "psycopg_binary-3.2.12-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dc68094e00a5a7e8c20de1d3a0d5e404a27f522e18f8eb62bbbc9f865c3c81ef"},
{file = "psycopg_binary-3.2.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2d55009eeddbef54c711093c986daaf361d2c4210aaa1ee905075a3b97a62441"},
{file = "psycopg_binary-3.2.12-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66a031f22e4418016990446d3e38143826f03ad811b9f78f58e2afbc1d343f7a"},
{file = "psycopg_binary-3.2.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:58ed30d33c25d7dc8d2f06285e88493147c2a660cc94713e4b563a99efb80a1f"},
{file = "psycopg_binary-3.2.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0b5ccd03ca4749b8f66f38608ccbcb415cbd130d02de5eda80d042b83bee90e"},
{file = "psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:909de94de7dd4d6086098a5755562207114c9638ec42c52d84c8a440c45fe084"},
{file = "psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7130effd0517881f3a852eff98729d51034128f0737f64f0d1c7ea8343d77bd7"},
{file = "psycopg_binary-3.2.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89b3c5201ca616d69ca0c3c0003ca18f7170a679c445c7e386ebfb4f29aa738e"},
{file = "psycopg_binary-3.2.12-cp314-cp314-win_amd64.whl", hash = "sha256:48a8e29f3e38fcf8d393b8fe460d83e39c107ad7e5e61cd3858a7569e0554a39"},
{file = "psycopg_binary-3.2.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2aa80ca8d17266507bef853cecefa7d632ffd087883ee7ca92b8a7ea14a1e581"},
{file = "psycopg_binary-3.2.12-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:deeb06b7141f3a577c3aa8562307e2747580ae43d705a0482603a2c1f110d046"},
{file = "psycopg_binary-3.2.12-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:32b3e12d9441508f9c4e1424f4478b1a518a90a087cd54be3754e74954934194"},
{file = "psycopg_binary-3.2.12-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d7cedecbe0bb60a2e72b1613fba4072a184a6472d6cc9aa99e540217f544e3e"},
{file = "psycopg_binary-3.2.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea049c8d33c4f4e6b030d5a68123c0ccd2ffb77d4035f073db97187b49b6422f"},
{file = "psycopg_binary-3.2.12-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f821e0c8a8fdfddfa71acb4f462d7a4c5aae1655f3f5e078970dbe9f19027386"},
{file = "psycopg_binary-3.2.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ef40601b959cc1440deaf4d53472ab54fa51036c37189cf3fe5500559ac25347"},
{file = "psycopg_binary-3.2.12-cp38-cp38-win_amd64.whl", hash = "sha256:0afb71a99871a41dd677d207c6a988d978edde5d6a018bafaed4f9da45357055"},
{file = "psycopg_binary-3.2.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8107968a9eadb451cfa6cf86036006fdde32a83cd39c26c9ca46765e653b547"},
{file = "psycopg_binary-3.2.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15e226f0d8af85cc8b2435b2e9bc6f0d40febc79eef76cf20fceac4d902a6a7b"},
{file = "psycopg_binary-3.2.12-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f6ba1fe35fd215813dac4544a5ffc90f13713b29dd26e9e5be97ba53482bf6d6"},
{file = "psycopg_binary-3.2.12-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:26b5927b5880b396231ab6190ee5c8fb47ed3f459b53504ed5419faaf16d3bfb"},
{file = "psycopg_binary-3.2.12-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab02b7d138768fd6ac4230e45b073f7b9fd688d88c04f24c34df4a250a94d066"},
{file = "psycopg_binary-3.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:acb1811219a4144539f0baee224a11a2aa323a739c349799cf52f191eb87bc52"},
{file = "psycopg_binary-3.2.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:356b4266e5cde7b5bbcf232f549dedf7fbed4983daa556042bdec397780e044d"},
{file = "psycopg_binary-3.2.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:489b154891f1c995355adeb1077ee3479e9c9bada721b93270c20243bbad6542"},
{file = "psycopg_binary-3.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:294f08b014f08dfd3c9b72408f5e1a0fd187bd86d7a85ead651e32dbd47aa038"},
{file = "psycopg_binary-3.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b3385b58b2fe408a13d084c14b8dcf468cd36cbbe774408250facc128f9fa75c"},
{file = "psycopg_binary-3.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1bef235a50a80f6aba05147002bc354559657cb6386dbd04d8e1c97d1d7cbe84"},
{file = "psycopg_binary-3.3.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:97c839717bf8c8df3f6d983a20949c4fb22e2a34ee172e3e427ede363feda27b"},
{file = "psycopg_binary-3.3.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:48e500cf1c0984dacf1f28ea482c3cdbb4c2288d51c336c04bc64198ab21fc51"},
{file = "psycopg_binary-3.3.3-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb36a08859b9432d94ea6b26ec41a2f98f83f14868c91321d0c1e11f672eeae7"},
{file = "psycopg_binary-3.3.3-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dde92cfde09293fb63b3f547919ba7d73bd2654573c03502b3263dd0218e44e"},
{file = "psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78c9ce98caaf82ac8484d269791c1b403d7598633e0e4e2fa1097baae244e2f1"},
{file = "psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d593612758d0041cb13cb0003f7f8d3fabb7ad9319e651e78afae49b1cf5860e"},
{file = "psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f24e8e17035200a465c178e9ea945527ad0738118694184c450f1192a452ff25"},
{file = "psycopg_binary-3.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e7b607f0e14f2a4cf7e78a05ebd13df6144acfba87cb90842e70d3f125d9f53f"},
{file = "psycopg_binary-3.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b27d3a23c79fa59557d2cc63a7e8bb4c7e022c018558eda36f9d7c4e6b99a6e0"},
{file = "psycopg_binary-3.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a89bb9ee11177b2995d87186b1d9fa892d8ea725e85eab28c6525e4cc14ee048"},
{file = "psycopg_binary-3.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f7d0cf072c6fbac3795b08c98ef9ea013f11db609659dcfc6b1f6cc31f9e181"},
{file = "psycopg_binary-3.3.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:90eecd93073922f085967f3ed3a98ba8c325cbbc8c1a204e300282abd2369e13"},
{file = "psycopg_binary-3.3.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dac7ee2f88b4d7bb12837989ca354c38d400eeb21bce3b73dac02622f0a3c8d6"},
{file = "psycopg_binary-3.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62cf8784eb6d35beaee1056d54caf94ec6ecf2b7552395e305518ab61eb8fd2"},
{file = "psycopg_binary-3.3.3-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a39f34c9b18e8f6794cca17bfbcd64572ca2482318db644268049f8c738f35a6"},
{file = "psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:883d68d48ca9ff3cb3d10c5fdebea02c79b48eecacdddbf7cce6e7cdbdc216b8"},
{file = "psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:cab7bc3d288d37a80aa8c0820033250c95e40b1c2b5c57cf59827b19c2a8b69d"},
{file = "psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:56c767007ca959ca32f796b42379fc7e1ae2ed085d29f20b05b3fc394f3715cc"},
{file = "psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:da2f331a01af232259a21573a01338530c6016dcfad74626c01330535bcd8628"},
{file = "psycopg_binary-3.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:19f93235ece6dbfc4036b5e4f6d8b13f0b8f2b3eeb8b0bd2936d406991bcdd40"},
{file = "psycopg_binary-3.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6698dbab5bcef8fdb570fc9d35fd9ac52041771bfcfe6fd0fc5f5c4e36f1e99d"},
{file = "psycopg_binary-3.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:329ff393441e75f10b673ae99ab45276887993d49e65f141da20d915c05aafd8"},
{file = "psycopg_binary-3.3.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:eb072949b8ebf4082ae24289a2b0fd724da9adc8f22743409d6fd718ddb379df"},
{file = "psycopg_binary-3.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:263a24f39f26e19ed7fc982d7859a36f17841b05bebad3eb47bb9cd2dd785351"},
{file = "psycopg_binary-3.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5152d50798c2fa5bd9b68ec68eb68a1b71b95126c1d70adaa1a08cd5eefdc23d"},
{file = "psycopg_binary-3.3.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d6a1e56dd267848edb824dbeb08cf5bac649e02ee0b03ba883ba3f4f0bd54f2"},
{file = "psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73eaaf4bb04709f545606c1db2f65f4000e8a04cdbf3e00d165a23004692093e"},
{file = "psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:162e5675efb4704192411eaf8e00d07f7960b679cd3306e7efb120bb8d9456cc"},
{file = "psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:fab6b5e37715885c69f5d091f6ff229be71e235f272ebaa35158d5a46fd548a0"},
{file = "psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a4aab31bd6d1057f287c96c0effca3a25584eb9cc702f282ecb96ded7814e830"},
{file = "psycopg_binary-3.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:59aa31fe11a0e1d1bcc2ce37ed35fe2ac84cd65bb9036d049b1a1c39064d0f14"},
{file = "psycopg_binary-3.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05f32239aec25c5fb15f7948cffdc2dc0dac098e48b80a140e4ba32b572a2e7d"},
{file = "psycopg_binary-3.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c84f9d214f2d1de2fafebc17fa68ac3f6561a59e291553dfc45ad299f4898c1"},
{file = "psycopg_binary-3.3.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e77957d2ba17cada11be09a5066d93026cdb61ada7c8893101d7fe1c6e1f3925"},
{file = "psycopg_binary-3.3.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:42961609ac07c232a427da7c87a468d3c82fee6762c220f38e37cfdacb2b178d"},
{file = "psycopg_binary-3.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae07a3114313dd91fce686cab2f4c44af094398519af0e0f854bc707e1aeedf1"},
{file = "psycopg_binary-3.3.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d257c58d7b36a621dcce1d01476ad8b60f12d80eb1406aee4cf796f88b2ae482"},
{file = "psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07c7211f9327d522c9c47560cae00a4ecf6687f4e02d779d035dd3177b41cb12"},
{file = "psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8e7e9eca9b363dbedeceeadd8be97149d2499081f3c52d141d7cd1f395a91f83"},
{file = "psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:cb85b1d5702877c16f28d7b92ba030c1f49ebcc9b87d03d8c10bf45a2f1c7508"},
{file = "psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d4606c84d04b80f9138d72f1e28c6c02dc5ae0c7b8f3f8aaf89c681ce1cd1b1"},
{file = "psycopg_binary-3.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:74eae563166ebf74e8d950ff359be037b85723d99ca83f57d9b244a871d6c13b"},
{file = "psycopg_binary-3.3.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:497852c5eaf1f0c2d88ab74a64a8097c099deac0c71de1cbcf18659a8a04a4b2"},
{file = "psycopg_binary-3.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:258d1ea53464d29768bf25930f43291949f4c7becc706f6e220c515a63a24edd"},
{file = "psycopg_binary-3.3.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:111c59897a452196116db12e7f608da472fbff000693a21040e35fc978b23430"},
{file = "psycopg_binary-3.3.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:17bb6600e2455993946385249a3c3d0af52cd70c1c1cdbf712e9d696d0b0bf1b"},
{file = "psycopg_binary-3.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:642050398583d61c9856210568eb09a8e4f2fe8224bf3be21b67a370e677eead"},
{file = "psycopg_binary-3.3.3-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:533efe6dc3a7cba5e2a84e38970786bb966306863e45f3db152007e9f48638a6"},
{file = "psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5958dbf28b77ce2033482f6cb9ef04d43f5d8f4b7636e6963d5626f000efb23e"},
{file = "psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a6af77b6626ce92b5817bf294b4d45ec1a6161dba80fc2d82cdffdd6814fd023"},
{file = "psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:47f06fcbe8542b4d96d7392c476a74ada521c5aebdb41c3c0155f6595fc14c8d"},
{file = "psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7800e6c6b5dc4b0ca7cc7370f770f53ac83886b76afda0848065a674231e856"},
{file = "psycopg_binary-3.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:165f22ab5a9513a3d7425ffb7fcc7955ed8ccaeef6d37e369d6cc1dff1582383"},
]
[[package]]
@@ -1760,14 +1751,14 @@ files = [
[[package]]
name = "pydantic"
version = "2.12.4"
version = "2.12.5"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
{file = "pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e"},
{file = "pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac"},
{file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"},
{file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"},
]
[package.dependencies]
@@ -1943,14 +1934,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pyjwt"
version = "2.10.1"
version = "2.11.0"
description = "JSON Web Token implementation in Python"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"},
{file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"},
{file = "pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469"},
{file = "pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623"},
]
[package.dependencies]
@@ -1958,9 +1949,9 @@ cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"cryp
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
dev = ["coverage[toml] (==7.10.7)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=8.4.2,<9.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
tests = ["coverage[toml] (==7.10.7)", "pytest (>=8.4.2,<9.0.0)"]
[[package]]
name = "pyquery"
@@ -2275,39 +2266,39 @@ files = [
[[package]]
name = "social-auth-app-django"
version = "5.6.0"
version = "5.7.0"
description = "Python Social Authentication, Django integration."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "social_auth_app_django-5.6.0-py3-none-any.whl", hash = "sha256:43ca88cc5cd9161710896165ced58b3155e8aafaaff847e859879194770f138d"},
{file = "social_auth_app_django-5.6.0.tar.gz", hash = "sha256:c695501fcbf6fe87f68f5a79e379abe853662f5129e7ec6cb758a75d5b28c888"},
{file = "social_auth_app_django-5.7.0-py3-none-any.whl", hash = "sha256:492b6f64e1e9fa5ed62b24bc19bde31c235fd4eb7fbd5c310e999e752b8f699a"},
{file = "social_auth_app_django-5.7.0.tar.gz", hash = "sha256:c0cc118d1bb935dbf02acb8a57fabd7b07694452fce755a5956282c69f019c79"},
]
[package.dependencies]
Django = ">=5.1"
social-auth-core = ">=4.4,<5.0"
social-auth-core = ">=4.8.3,<4.9.0"
[package.extras]
dev = ["coverage (>=3.6)", "django-stubs (==5.2.7)", "django-stubs-ext (==5.2.7)", "mypy (==1.18.2)", "pre-commit", "pyright (==1.1.406)", "tox"]
dev = ["coverage (>=3.6)", "django-stubs (==5.2.8)", "django-stubs-ext (==5.2.8)", "mypy (==1.19.1)", "pre-commit", "pyright (==1.1.407)", "tox"]
[[package]]
name = "social-auth-core"
version = "4.8.1"
version = "4.8.5"
description = "Python social authentication made simple."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "social_auth_core-4.8.1-py3-none-any.whl", hash = "sha256:9fe54f7c7d566465ae34b165bfe1c0d3ba8fa1f7042dc17df5e7dcef8675f3a0"},
{file = "social_auth_core-4.8.1.tar.gz", hash = "sha256:6186576eb4e9f25c789d0ce19b942cc668a3a714a246f9e688c0fb93f65bf111"},
{file = "social_auth_core-4.8.5-py3-none-any.whl", hash = "sha256:2591c2ce71127ad410e7ca9581bd88658031fdf7b209e05be5920d0bcc1c005a"},
{file = "social_auth_core-4.8.5.tar.gz", hash = "sha256:fd10d44bff681a128d127f665f203c496658d5bbfc993ad1b5bbaed589eab573"},
]
[package.dependencies]
defusedxml = ">=0.7.1"
oauthlib = ">=3.3.1"
PyJWT = {version = ">=2.10.1", extras = ["crypto"]}
PyJWT = {version = ">=2.11.0", extras = ["crypto"]}
python3-openid = ">=3.2.0"
requests = ">=2.32.5"
requests-oauthlib = ">=2.0.0"
@@ -2316,76 +2307,82 @@ requests-oauthlib = ">=2.0.0"
all = ["social-auth-core[azuread]", "social-auth-core[google-onetap]", "social-auth-core[saml]", "social-auth-core[shopify]"]
allpy3 = ["social-auth-core[all]"]
azuread = ["cryptography (>=42.0.8)"]
dev = ["coverage (==7.10.7)", "flake8 (==7.3.0)", "google-auth-stubs (==0.3.0)", "mypy (==1.18.2)", "pyright (==1.1.406)", "pytest (==8.4.2)", "pytest-cov (==7.0.0)", "pytest-github-actions-annotate-failures (==0.3.0)", "pytest-profiling (==1.8.1)", "pytest-xdist (==3.8.0)", "responses (==0.25.8)", "ty (==0.0.1a21)", "types-defusedxml (==0.7.0.20250822)", "types-oauthlib (==3.3.0.20250822)", "types-requests (==2.32.4.20250913)", "types-requests-oauthlib (==2.0.0.20250809)"]
google-onetap = ["google-auth (>=2.40.0,<2.42)"]
dev = ["coverage (==7.13.4)", "flake8 (==7.3.0)", "google-auth-stubs (==0.3.0)", "mypy (==1.19.1)", "pyright (==1.1.408)", "pytest (==9.0.2)", "pytest-cov (==7.0.0)", "pytest-github-actions-annotate-failures (==0.3.0)", "pytest-profiling (==1.8.1)", "pytest-xdist (==3.8.0)", "responses (==0.25.8)", "ty (==0.0.15)", "types-defusedxml (==0.7.0.20250822)", "types-oauthlib (==3.3.0.20250822)", "types-requests (==2.32.4.20260107)", "types-requests-oauthlib (==2.0.0.20250809)"]
google-onetap = ["google-auth (>=2.40.0,<2.49)"]
saml = ["python3-saml (>=1.16.0)"]
shopify = ["ShopifyAPI"]
[[package]]
name = "sqlalchemy"
version = "2.0.44"
version = "2.0.48"
description = "Database Abstraction Library"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "SQLAlchemy-2.0.44-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:471733aabb2e4848d609141a9e9d56a427c0a038f4abf65dd19d7a21fd563632"},
{file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48bf7d383a35e668b984c805470518b635d48b95a3c57cb03f37eaa3551b5f9f"},
{file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf4bb6b3d6228fcf3a71b50231199fb94d2dd2611b66d33be0578ea3e6c2726"},
{file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:e998cf7c29473bd077704cea3577d23123094311f59bdc4af551923b168332b1"},
{file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ebac3f0b5732014a126b43c2b7567f2f0e0afea7d9119a3378bde46d3dcad88e"},
{file = "SQLAlchemy-2.0.44-cp37-cp37m-win32.whl", hash = "sha256:3255d821ee91bdf824795e936642bbf43a4c7cedf5d1aed8d24524e66843aa74"},
{file = "SQLAlchemy-2.0.44-cp37-cp37m-win_amd64.whl", hash = "sha256:78e6c137ba35476adb5432103ae1534f2f5295605201d946a4198a0dea4b38e7"},
{file = "sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce"},
{file = "sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985"},
{file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0"},
{file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e"},
{file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749"},
{file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2"},
{file = "sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165"},
{file = "sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5"},
{file = "sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd"},
{file = "sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa"},
{file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e"},
{file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e"},
{file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399"},
{file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b"},
{file = "sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3"},
{file = "sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5"},
{file = "sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250"},
{file = "sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29"},
{file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44"},
{file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1"},
{file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7"},
{file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d"},
{file = "sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4"},
{file = "sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e"},
{file = "sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1"},
{file = "sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45"},
{file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976"},
{file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c"},
{file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d"},
{file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40"},
{file = "sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73"},
{file = "sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e"},
{file = "sqlalchemy-2.0.44-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fc44e5965ea46909a416fff0af48a219faefd5773ab79e5f8a5fcd5d62b2667"},
{file = "sqlalchemy-2.0.44-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dc8b3850d2a601ca2320d081874033684e246d28e1c5e89db0864077cfc8f5a9"},
{file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d733dec0614bb8f4bcb7c8af88172b974f685a31dc3a65cca0527e3120de5606"},
{file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22be14009339b8bc16d6b9dc8780bacaba3402aa7581658e246114abbd2236e3"},
{file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:357bade0e46064f88f2c3a99808233e67b0051cdddf82992379559322dfeb183"},
{file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4848395d932e93c1595e59a8672aa7400e8922c39bb9b0668ed99ac6fa867822"},
{file = "sqlalchemy-2.0.44-cp38-cp38-win32.whl", hash = "sha256:2f19644f27c76f07e10603580a47278abb2a70311136a7f8fd27dc2e096b9013"},
{file = "sqlalchemy-2.0.44-cp38-cp38-win_amd64.whl", hash = "sha256:1df4763760d1de0dfc8192cc96d8aa293eb1a44f8f7a5fbe74caf1b551905c5e"},
{file = "sqlalchemy-2.0.44-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7027414f2b88992877573ab780c19ecb54d3a536bef3397933573d6b5068be4"},
{file = "sqlalchemy-2.0.44-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fe166c7d00912e8c10d3a9a0ce105569a31a3d0db1a6e82c4e0f4bf16d5eca9"},
{file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3caef1ff89b1caefc28f0368b3bde21a7e3e630c2eddac16abd9e47bd27cc36a"},
{file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc2856d24afa44295735e72f3c75d6ee7fdd4336d8d3a8f3d44de7aa6b766df2"},
{file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:11bac86b0deada30b6b5f93382712ff0e911fe8d31cb9bf46e6b149ae175eff0"},
{file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d18cd0e9a0f37c9f4088e50e3839fcb69a380a0ec957408e0b57cff08ee0a26"},
{file = "sqlalchemy-2.0.44-cp39-cp39-win32.whl", hash = "sha256:9e9018544ab07614d591a26c1bd4293ddf40752cc435caf69196740516af7100"},
{file = "sqlalchemy-2.0.44-cp39-cp39-win_amd64.whl", hash = "sha256:8e0e4e66fd80f277a8c3de016a81a554e76ccf6b8d881ee0b53200305a8433f6"},
{file = "sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05"},
{file = "sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22"},
{file = "sqlalchemy-2.0.48-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89"},
{file = "sqlalchemy-2.0.48-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0"},
{file = "sqlalchemy-2.0.48-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd"},
{file = "sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29"},
{file = "sqlalchemy-2.0.48-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0"},
{file = "sqlalchemy-2.0.48-cp310-cp310-win32.whl", hash = "sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018"},
{file = "sqlalchemy-2.0.48-cp310-cp310-win_amd64.whl", hash = "sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76"},
{file = "sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc"},
{file = "sqlalchemy-2.0.48-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c"},
{file = "sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7"},
{file = "sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d"},
{file = "sqlalchemy-2.0.48-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571"},
{file = "sqlalchemy-2.0.48-cp311-cp311-win32.whl", hash = "sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617"},
{file = "sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl", hash = "sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c"},
{file = "sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b"},
{file = "sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb"},
{file = "sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894"},
{file = "sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9"},
{file = "sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e"},
{file = "sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99"},
{file = "sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a"},
{file = "sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4"},
{file = "sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f"},
{file = "sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed"},
{file = "sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658"},
{file = "sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8"},
{file = "sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131"},
{file = "sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2"},
{file = "sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae"},
{file = "sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb"},
{file = "sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b"},
{file = "sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121"},
{file = "sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485"},
{file = "sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79"},
{file = "sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd"},
{file = "sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f"},
{file = "sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b"},
{file = "sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0"},
{file = "sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2"},
{file = "sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6"},
{file = "sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0"},
{file = "sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241"},
{file = "sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0"},
{file = "sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3"},
{file = "sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b"},
{file = "sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f"},
{file = "sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933"},
{file = "sqlalchemy-2.0.48-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e"},
{file = "sqlalchemy-2.0.48-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616"},
{file = "sqlalchemy-2.0.48-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7"},
{file = "sqlalchemy-2.0.48-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b"},
{file = "sqlalchemy-2.0.48-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67"},
{file = "sqlalchemy-2.0.48-cp38-cp38-win32.whl", hash = "sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad"},
{file = "sqlalchemy-2.0.48-cp38-cp38-win_amd64.whl", hash = "sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565"},
{file = "sqlalchemy-2.0.48-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77"},
{file = "sqlalchemy-2.0.48-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457"},
{file = "sqlalchemy-2.0.48-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e"},
{file = "sqlalchemy-2.0.48-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a"},
{file = "sqlalchemy-2.0.48-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6"},
{file = "sqlalchemy-2.0.48-cp39-cp39-win32.whl", hash = "sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5"},
{file = "sqlalchemy-2.0.48-cp39-cp39-win_amd64.whl", hash = "sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099"},
{file = "sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096"},
{file = "sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7"},
]
[package.dependencies]
@@ -2508,6 +2505,24 @@ files = [
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
]
[[package]]
name = "tzlocal"
version = "5.3.1"
description = "tzinfo object for the local timezone"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d"},
{file = "tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd"},
]
[package.dependencies]
tzdata = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
[[package]]
name = "urllib3"
version = "2.5.0"
@@ -2580,4 +2595,4 @@ brotli = ["brotli"]
[metadata]
lock-version = "2.1"
python-versions = "^3.13"
content-hash = "4ebe15a96f351d1f4e2d117f7a20d158ba98a48cf09d5b75d3b260e5c0e386bd"
content-hash = "d9a76e68aa1a65fe555ec66beb709358e6d5be848ded1d077d20e87533f70c01"

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "hotpocket-backend"
version = "25.11.19"
version = "26.3.16"
description = "HotPocket Backend"
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
license = "Apache-2.0"
@@ -14,22 +14,22 @@ priority = "supplemental"
[tool.poetry.dependencies]
python = "^3.13"
bthlabs-jsonrpc-django = "1.2.0"
celery = "5.5.3"
crispy-bootstrap5 = "2025.6"
django = "5.2.8"
celery = "5.6.2"
crispy-bootstrap5 = "2026.3"
django = "5.2.12"
django-cors-headers = "4.9.0"
django-crispy-forms = "2.5"
django-htmx = "1.26.0"
django-crispy-forms = "2.6"
django-htmx = "1.27.0"
hotpocket-common = {path = "../packages/common", develop = true}
hotpocket-soa = {path = "../packages/soa", develop = true}
keep-it-secret = {version = "1.3.0", extras = ["aws", "vault"]}
psycopg = {version = "3.2.12", extras = ["binary"]}
pydantic = "2.12.4"
psycopg = {version = "3.3.3", extras = ["binary"]}
pydantic = "2.12.5"
pyquery = "2.0.1"
requests = "2.32.5"
social-auth-app-django = "5.6.0"
social-auth-core = "4.8.1"
sqlalchemy = "2.0.44"
social-auth-app-django = "5.7.0"
social-auth-core = "4.8.5"
sqlalchemy = "2.0.48"
uuid6 = "2025.0.1"
[tool.poetry.group.dev.dependencies]

View File

@@ -103,7 +103,7 @@ def ci(ctx: Context):
@task
def setup(ctx: Context):
ctx.run('python manage.py migrate')
ctx.run('python manage.py create_initial_account hotpocket hotpocketm4st3r')
ctx.run('python manage.py create_initial_account -u hotpocket -p hotpocketm4st3r')
if WORKSPACE_MODE == WorkspaceMode.METAL:
ctx.run('mkdir -p run/uploads')

View File

@@ -8,7 +8,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.13"
pydantic = "2.12.4"
pydantic = "2.12.5"
[tool.poetry.plugins.pytest11]
hotpocket_backend = "hotpocket_backend_testing.plugin"

View File

@@ -28,13 +28,15 @@ def test_authenticated_ok(authenticated_client: Client,
assert result.context['association'].target.pk == association_out.target.pk
assert result.context['show_controls'] is True
assert 'share_url' in result.context
assert result.context['is_share'] is False
assert result.context['og_card_url'] is None
expected_share_url = reverse(
'ui.associations.view',
args=(association_out.pk,),
query=[('share', 'true')],
)
assert result.context['share_url'] == expected_share_url
assert result.context['share_url'].endswith(expected_share_url)
@pytest.mark.django_db
@@ -126,6 +128,16 @@ def test_authenticated_share_ok(authenticated_client: Client,
assert hasattr(result.context['association'], 'target') is True
assert result.context['association'].target.pk == other_account_association_out.target.pk
assert result.context['show_controls'] is False
assert 'share_url' in result.context
assert result.context['is_share'] is True
assert result.context['og_card_url'] is not None
expected_share_url = reverse(
'ui.associations.view',
args=(other_account_association_out.pk,),
query=[('share', 'true')],
)
assert result.context['share_url'].endswith(expected_share_url)
@pytest.mark.django_db
@@ -313,7 +325,19 @@ def test_inactive_account(inactive_account_client: Client,
)
# Then
assert result.status_code == http.HTTPStatus.FORBIDDEN
asserts.assertRedirects(
result,
reverse(
'ui.accounts.login',
query=[
(
'next',
reverse('ui.associations.view', args=(association_out.pk,)),
),
],
),
fetch_redirect_response=False,
)
@pytest.mark.django_db
@@ -326,4 +350,16 @@ def test_anonymous(client: Client,
)
# Then
assert result.status_code == http.HTTPStatus.FORBIDDEN
asserts.assertRedirects(
result,
reverse(
'ui.accounts.login',
query=[
(
'next',
reverse('ui.associations.view', args=(association_out.pk,)),
),
],
),
fetch_redirect_response=False,
)

View File

@@ -54,8 +54,12 @@ def test_ok(authenticated_client: Client,
call_result = result.json()
assert 'error' not in call_result
save_pk = uuid.UUID(call_result['result']['target_uuid'])
association_pk = uuid.UUID(call_result['result']['id'])
save_pk = uuid.UUID(call_result['result']['target_uuid'])
assert call_result['result']['url'].endswith(reverse(
'ui.associations.view', args=(association_pk,),
))
AssociationsTestingService().assert_created(
pk=association_pk,

View File

@@ -30,5 +30,9 @@
"content_popup_content_error_message": {
"message": "HotPocket couldn't complete this operation.",
"description": "Title of the error content popup."
},
"content_popup_content_view_button_message": {
"message": "View in HotPocket",
"description": "Title of the view link button"
}
}

View File

@@ -2,6 +2,7 @@ run:
echo: true
pty: true
files_to_version:
- "src/content/options.html"
- "src/content/preauth.html"
- "src/manifest/common.json"
- "package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "hotpocket-extension",
"version": "25.11.19",
"version": "26.3.16",
"description": "HotPocket Extension",
"main": "src/index.js",
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
@@ -18,7 +18,10 @@
"watch:chrome": "HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js -w",
"build:firefox": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js",
"dev:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js",
"watch:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js -w"
"watch:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js -w",
"build:opera": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=opera npx rollup -c rollup.config.js",
"dev:opera": "HOTPOCKET_EXTENSION_TARGET=opera npx rollup -c rollup.config.js",
"watch:opera": "HOTPOCKET_EXTENSION_TARGET=opera npx rollup -c rollup.config.js -w"
},
"devDependencies": {
"@eslint/js": "9.33.0",

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "hotpocket-extension"
version = "25.11.19"
version = "26.3.16"
description = "HotPocket Extension"
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
license = "Apache-2.0"

View File

@@ -6,6 +6,7 @@ import packageJSON from './package.json' with {type: 'json'};
import manifestChrome from './src/manifest/chrome.json' with {type: 'json'};
import manifestCommon from './src/manifest/common.json' with {type: 'json'};
import manifestFirefox from './src/manifest/firefox.json' with {type: 'json'};
import manifestOpera from './src/manifest/opera.json' with {type: 'json'};
import manifestSafari from './src/manifest/safari.json' with {type: 'json'};
const BANNER = `/*!
@@ -66,6 +67,11 @@ const manifestJsonOutputPlugin = () => {
...result,
...manifestFirefox,
};
} else if (TARGET == 'opera') {
result = {
...result,
...manifestOpera,
};
}
result.version = packageJSON.version;
@@ -110,6 +116,8 @@ export default [
},
{
src: [
'src/content/options.html',
'src/content/options.js',
'src/content/preauth.html',
'src/content/preauth.js',
],

View File

@@ -8,13 +8,21 @@ let authSessionToken = null;
let accountsRPCURL = null;
let rpcURL = null;
const updateRpcURLs = () => {
const updateRpcURLs = (options) => {
options = {
clear: false,
...(options || {}),
};
accountsRPCURL = null;
rpcURL = null;
if (HotPocketExtension.base_url !== null) {
rpcURL = (new URL(RPC_PATH, HotPocketExtension.base_url)).toString();
accountsRPCURL = (new URL(ACCOUNTS_RPC_PATH, HotPocketExtension.base_url)).toString();
} else if (options.clear === true) {
rpcURL = null;
accountsRPCURL = null;
}
HotPocketExtension.LOGGER.debug(
@@ -117,10 +125,10 @@ const doSave = async (accessToken, tab) => {
);
if (error !== null) {
return false;
return null;
}
return true;
return result;
};
const doCreateAndStoreAccessToken = async (authKey) => {
@@ -355,6 +363,12 @@ const onBrowserActionClicked = async (tab) => {
return await doSendTabMessage(tab, message);
};
const doLogOut = async () => {
// eslint-disable-next-line no-unused-vars
let storageResult = await HotPocketExtension.api.storage.local.clear();
updateRpcURLs({clear: true});
};
const onMessage = (message, sender, sendResponse) => {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.onMessage()', message, sender, sendResponse,
@@ -368,6 +382,10 @@ const onMessage = (message, sender, sendResponse) => {
});
} else if (message.type === 'HotPocket:Extension:setBaseURL') {
doUpdateBaseURL(message.result);
} else if (message.type === 'HotPocket:ExtensionOptions:logOut') {
doLogOut();
} else if (message.type === 'HotPocket:ExtensionOptions:logIn') {
doSetupRPC();
}
} catch (exception) {
HotPocketExtension.LOGGER.error(

View File

@@ -0,0 +1,6 @@
import main from './main';
main({
platform: 'Opera',
api: chrome,
});

View File

@@ -26,7 +26,7 @@ class Popup {
element.insertAdjacentHTML('beforeend', content);
};
setContent = (content) => {
setContent = (content, saveUrl) => {
const shadow = this.container.shadowRoot;
const body = shadow.querySelector('.hotpocket-extension-popup-body');
@@ -38,6 +38,13 @@ class Popup {
i18nElement.dataset.message,
);
}
if (saveUrl) {
const viewLink = shadow.querySelector('.hotpocket-extension-popup-view');
if (viewLink) {
viewLink.href = saveUrl;
}
}
};
close = () => {
this.clearCloseTimeout();
@@ -47,7 +54,7 @@ class Popup {
this.container = null;
}
};
show = (content) => {
show = (content, saveUrl) => {
this.close();
this.container = document.createElement('div');
@@ -57,7 +64,7 @@ class Popup {
const shadow = this.container.attachShadow({mode: 'open'});
shadow.setHTMLUnsafe(POPUP);
this.setContent(content);
this.setContent(content, saveUrl);
const closeElements = shadow.querySelectorAll('.hotpocket-extension-popup-close');
for (const closeElement of closeElements) {
@@ -66,8 +73,8 @@ class Popup {
document.body.appendChild(this.container);
};
update = (content) => {
this.setContent(content);
update = (content, saveUrl) => {
this.setContent(content, saveUrl);
};
onCloseClick = (event) => {
this.close();
@@ -86,16 +93,16 @@ const doHandleBrowserActionClickedMessage = (message) => {
};
const doHandleSaveMessage = (message) => {
let content = POPUP_CONTENT_ERROR;
if (message.result === true) {
content = POPUP_CONTENT_SUCCESS;
let content = POPUP_CONTENT_SUCCESS;
if (message.result === null) {
content = POPUP_CONTENT_ERROR;
}
if (currentPopup === null) {
currentPopup = new Popup();
currentPopup.show(content);
} else {
currentPopup.update(content);
currentPopup.update(content, (message.result || {}).url);
}
};

View File

@@ -0,0 +1,6 @@
import main from './main';
main({
platform: 'Opera',
api: window.chrome,
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,118 @@
/*!
* HotPocket by BTHLabs (https://hotpocket.app/)
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(() => {
'use strict';
document.addEventListener('DOMContentLoaded', (event) => {
const form = document.getElementById('OptionsForm');
const fieldBaseURL = document.getElementById('div_id_base_url');
const fieldNeedsSetup = document.getElementById('div_id_needs_setup');
const inputBaseURL = document.getElementById('id_base_url');
const inputSubmit = document.getElementById('id_submit');
let formLoader = form.querySelector('.loader');
let api = window.browser || null;
if (api === null && window.chrome) {
api = window.chrome;
}
const updateForm = () => {
if (inputBaseURL.value) {
fieldBaseURL.classList.remove('d-none');
fieldNeedsSetup.classList.add('d-none');
inputSubmit.classList.remove('btn-primary');
inputSubmit.classList.add('btn-secondary');
inputSubmit.value = 'Log out';
} else {
fieldBaseURL.classList.add('d-none');
fieldNeedsSetup.classList.remove('d-none');
inputSubmit.classList.remove('btn-secondary');
inputSubmit.classList.add('btn-primary');
inputSubmit.value = 'Log in';
}
};
const loadBaseURL = async (options) => {
options = {
initial: false,
...(options || {}),
};
formLoader.classList.remove('d-none');
const storageResult = await api.storage.local.get(['baseURL']);
if (storageResult.baseURL) {
inputBaseURL.value = storageResult.baseURL;
}
if (options.initial === true) {
formLoader.classList.add('d-none');
}
updateForm();
return storageResult.baseURL || null;
};
const startPollingBaseURL = async () => {
let timeout = null;
formLoader.classList.remove('d-none');
window.setTimeout(
async () => {
window.clearTimeout(timeout);
const result = await loadBaseURL();
if (result === null) {
startPollingBaseURL();
} else {
formLoader.classList.add('d-none');
}
},
5000,
);
};
form.addEventListener('submit', (event) => {
event.stopPropagation();
event.preventDefault();
if (inputBaseURL.value) {
api.runtime.sendMessage({
type: 'HotPocket:ExtensionOptions:logOut',
});
inputBaseURL.value = '';
} else {
api.runtime.sendMessage({
type: 'HotPocket:ExtensionOptions:logIn',
});
startPollingBaseURL();
}
updateForm();
return false;
});
loadBaseURL({initial: true});
});
})();

File diff suppressed because one or more lines are too long

View File

@@ -94,6 +94,26 @@
opacity: 0.83;
transform: rotate(60deg);
}
.hotpocket-extension-popup-view {
background-color: #1CBAED;
border: 1px solid #1CBAED;
border-radius: 0.25rem;
color: white;
display: inline-block;
font-size: 0.875rem;
line-height: 1.25;
margin-top: 0.25rem !important;
padding: 0.25rem 0.5rem;
text-decoration: none;
}
.hotpocket-extension-popup-view:hover {
background-color: #189ec9;
border-color: #1695be;
}
.hotpocket-extension-popup-view:active {
background-color: #1695be;
border-color: #158cb2;
}
@keyframes hotpocket-extension-popup-loader-animation {
100% {transform: rotate(1turn)}
}
@@ -104,6 +124,6 @@
<strong>HotPocket by BTHLabs</strong>
<a class="hotpocket-extension-popup-close">&times;</a>
</div>
<div class="hotpocket-extension-popup-body">
<div class="hotpocket-extension-popup-body hotpocket-extension-popup-message-success">
</div>
</div>

View File

@@ -2,4 +2,13 @@
<strong data-message="content_popup_content_success_title"></strong>
<br>
<span data-message="content_popup_content_success_message"></span>
<br>
<a
class="hotpocket-extension-popup-view"
data-message="content_popup_content_view_button_message"
href="#"
rel="noopener noreferrer"
target="_blank"
>
</a>
</p>

View File

@@ -3,7 +3,7 @@
"default_locale": "en",
"name": "__MSG_extension_name__",
"description": "__MSG_extension_description__",
"version": "25.11.19",
"version": "26.3.16",
"icons": {
"16": "images/icon-16.png",
"32": "images/icon-32.png",
@@ -28,5 +28,9 @@
"storage",
"activeTab",
"tabs"
]
],
"options_ui": {
"open_in_tab": true,
"page": "options.html"
}
}

View File

@@ -0,0 +1,13 @@
{
"action": {
"default_title": "__MSG_extension_name__",
"default_icon": {
"16": "images/toolbar-icon-16.png",
"32": "images/toolbar-icon-32.png"
}
},
"background": {
"service_worker": "background-bundle.js",
"type": "module"
}
}

View File

@@ -3,7 +3,6 @@
from __future__ import annotations
import json
import os
import typing
@@ -14,7 +13,10 @@ import werkzeug
import werkzeug.routing
from hotpocket_workspace_tools import get_workspace_mode
from hotpocket_workspace_tools.tasks import bump_version, get_version # noqa: F401
from hotpocket_workspace_tools.tasks import ( # noqa: F401
bump_version,
get_version,
)
WORKSPACE_MODE = get_workspace_mode()
@@ -249,3 +251,24 @@ def build_firefox_source(ctx: Context):
f'../firefox-source-{current_version}.zip',
'.',
]))
@task
def build_opera_source(ctx: Context):
# Opera requires source bundle to be uploaded alongside the built version.
ctx.run('rm -rf dist/opera-source')
ctx.run('mkdir -p dist/opera-source dist/opera-source/assets dist/opera-source/src')
ctx.run('rsync -arv assets/ dist/opera-source/assets/')
ctx.run('rsync -arv src/ dist/opera-source/src/')
ctx.run('rsync -arv eslint.config.js package.json README.md rollup.config.js yarn.lock dist/opera-source/')
with ctx.cd('dist/opera-source'):
current_version = get_version(ctx)
ctx.run(' '.join([
'zip',
'-r',
f'../opera-source-{current_version}.zip',
'.',
]))

View File

@@ -8,7 +8,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.13"
django = "5.2.8"
django = "5.2.12"
uuid6 = "2025.0.1"
[build-system]

View File

@@ -70,14 +70,14 @@ files = [
[[package]]
name = "django"
version = "5.2.8"
version = "5.2.12"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "django-5.2.8-py3-none-any.whl", hash = "sha256:37e687f7bd73ddf043e2b6b97cfe02fcbb11f2dbb3adccc6a2b18c6daa054d7f"},
{file = "django-5.2.8.tar.gz", hash = "sha256:23254866a5bb9a2cfa6004e8b809ec6246eba4b58a7589bc2772f1bcc8456c7f"},
{file = "django-5.2.12-py3-none-any.whl", hash = "sha256:4853482f395c3a151937f6991272540fcbf531464f254a347bf7c89f53c8cff7"},
{file = "django-5.2.12.tar.gz", hash = "sha256:6b809af7165c73eff5ce1c87fdae75d4da6520d6667f86401ecf55b681eb1eeb"},
]
[package.dependencies]
@@ -196,7 +196,7 @@ files = []
develop = true
[package.dependencies]
django = "5.2.8"
django = "5.2.12"
uuid6 = "2025.0.1"
[package.source]
@@ -214,7 +214,7 @@ files = []
develop = true
[package.dependencies]
pydantic = "2.12.4"
pydantic = "2.12.5"
[package.source]
type = "directory"
@@ -605,14 +605,14 @@ files = [
[[package]]
name = "pydantic"
version = "2.12.4"
version = "2.12.5"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e"},
{file = "pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac"},
{file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"},
{file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"},
]
[package.dependencies]

View File

@@ -8,7 +8,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.13"
pydantic = "2.12.4"
pydantic = "2.12.5"
[build-system]
requires = ["poetry-core"]