You've already forked hotpocket
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29c732faa0 | |||
| dcebccf947 | |||
| 67138c7035 |
@@ -66,7 +66,7 @@ $ docker run --rm -it \
|
|||||||
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME=hotpocket \
|
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME=hotpocket \
|
||||||
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD=hotpocketm4st3r \
|
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD=hotpocketm4st3r \
|
||||||
-p 8000:8000 \
|
-p 8000:8000 \
|
||||||
docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.8-01
|
docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.12-01
|
||||||
```
|
```
|
||||||
|
|
||||||
The command above will set up and start the application. The SQLite file will
|
The command above will set up and start the application. The SQLite file will
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.8-01"
|
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.12-01"
|
||||||
environment:
|
environment:
|
||||||
HOTPOCKET_BACKEND_SECRET_KEY: "thisisntright"
|
HOTPOCKET_BACKEND_SECRET_KEY: "thisisntright"
|
||||||
HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME: "hotpocket"
|
HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME: "hotpocket"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ x-backend-environment: &x-backend-environment
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
webapp:
|
webapp:
|
||||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.8-01"
|
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||||
environment:
|
environment:
|
||||||
<<: *x-backend-environment
|
<<: *x-backend-environment
|
||||||
HOTPOCKET_BACKEND_ALLOWED_HOSTS: "app.staging.hotpocket.bthlab.bthlabs.net"
|
HOTPOCKET_BACKEND_ALLOWED_HOSTS: "app.staging.hotpocket.bthlab.bthlabs.net"
|
||||||
@@ -21,7 +21,7 @@ services:
|
|||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.8-01"
|
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||||
environment:
|
environment:
|
||||||
<<: *x-backend-environment
|
<<: *x-backend-environment
|
||||||
HOTPOCKET_BACKEND_APP: "admin"
|
HOTPOCKET_BACKEND_APP: "admin"
|
||||||
@@ -35,7 +35,7 @@ services:
|
|||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
|
|
||||||
celery-worker:
|
celery-worker:
|
||||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.8-01"
|
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||||
command:
|
command:
|
||||||
- "/srv/venv/bin/celery"
|
- "/srv/venv/bin/celery"
|
||||||
- "-A"
|
- "-A"
|
||||||
@@ -57,7 +57,7 @@ services:
|
|||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
|
|
||||||
celery-beat:
|
celery-beat:
|
||||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.8-01"
|
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||||
command:
|
command:
|
||||||
- "/srv/venv/bin/celery"
|
- "/srv/venv/bin/celery"
|
||||||
- "-A"
|
- "-A"
|
||||||
|
|||||||
@@ -462,7 +462,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
|
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
|
||||||
@@ -474,12 +474,12 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.iOS.Extension;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -492,7 +492,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
|
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
|
||||||
@@ -504,12 +504,12 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.iOS.Extension;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -525,31 +525,36 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "iOS (App)/Info.plist";
|
INFOPLIST_FILE = "iOS (App)/Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
"-framework",
|
"-framework",
|
||||||
WebKit,
|
WebKit,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.iOS;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket;
|
||||||
PRODUCT_NAME = HotPocket;
|
PRODUCT_NAME = HotPocket;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
@@ -561,31 +566,36 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "iOS (App)/Info.plist";
|
INFOPLIST_FILE = "iOS (App)/Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
"-framework",
|
"-framework",
|
||||||
WebKit,
|
WebKit,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.iOS;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket;
|
||||||
PRODUCT_NAME = HotPocket;
|
PRODUCT_NAME = HotPocket;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
@@ -598,7 +608,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
@@ -612,12 +622,12 @@
|
|||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.macOS.Extension;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -629,9 +639,9 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
@@ -645,12 +655,12 @@
|
|||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.macOS.Extension;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.Extension;
|
||||||
PRODUCT_NAME = "HotPocket Extension";
|
PRODUCT_NAME = "HotPocket Extension";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -666,12 +676,13 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -679,14 +690,14 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
"-framework",
|
"-framework",
|
||||||
WebKit,
|
WebKit,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.macOS;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket;
|
||||||
PRODUCT_NAME = HotPocket;
|
PRODUCT_NAME = HotPocket;
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -700,14 +711,15 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 2025091201;
|
||||||
DEVELOPMENT_TEAM = 648728X64K;
|
DEVELOPMENT_TEAM = 648728X64K;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
INFOPLIST_KEY_CFBundleDisplayName = HotPocket;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025-present BTHLabs";
|
||||||
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -715,14 +727,14 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 25.9.8;
|
MARKETING_VERSION = 25.9.12;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-framework",
|
"-framework",
|
||||||
SafariServices,
|
SafariServices,
|
||||||
"-framework",
|
"-framework",
|
||||||
WebKit,
|
WebKit,
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.macOS;
|
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket;
|
||||||
PRODUCT_NAME = HotPocket;
|
PRODUCT_NAME = HotPocket;
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
version = '25.9.8'
|
version = '25.9.12'
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import uuid6
|
|||||||
|
|
||||||
from hotpocket_backend.apps.accounts.models import AccessToken
|
from hotpocket_backend.apps.accounts.models import AccessToken
|
||||||
from hotpocket_backend.apps.core.conf import settings
|
from hotpocket_backend.apps.core.conf import settings
|
||||||
from hotpocket_soa.dto.accounts import AccessTokensQuery
|
from hotpocket_soa.dto.accounts import (
|
||||||
|
AccessTokenMetaUpdateIn,
|
||||||
|
AccessTokensQuery,
|
||||||
|
)
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -54,6 +57,16 @@ class AccessTokensService:
|
|||||||
f'Access Token not found: pk=`{pk}`',
|
f'Access Token not found: pk=`{pk}`',
|
||||||
) from exception
|
) from exception
|
||||||
|
|
||||||
|
def get_by_key(self, *, key: str) -> AccessToken:
|
||||||
|
try:
|
||||||
|
query_set = AccessToken.active_objects
|
||||||
|
|
||||||
|
return query_set.get(key=key)
|
||||||
|
except AccessToken.DoesNotExist as exception:
|
||||||
|
raise self.AccessTokenNotFound(
|
||||||
|
f'Access Token not found: key=`{key}`',
|
||||||
|
) from exception
|
||||||
|
|
||||||
def search(self,
|
def search(self,
|
||||||
*,
|
*,
|
||||||
query: AccessTokensQuery,
|
query: AccessTokensQuery,
|
||||||
@@ -79,3 +92,27 @@ class AccessTokensService:
|
|||||||
access_token.soft_delete()
|
access_token.soft_delete()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def update_meta(self,
|
||||||
|
*,
|
||||||
|
pk: uuid.UUID,
|
||||||
|
update: AccessTokenMetaUpdateIn,
|
||||||
|
) -> AccessToken:
|
||||||
|
access_token = AccessToken.active_objects.get(pk=pk)
|
||||||
|
|
||||||
|
next_meta = {
|
||||||
|
**(access_token.meta or {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
if update.version is not None:
|
||||||
|
next_meta['version'] = update.version
|
||||||
|
|
||||||
|
if update.platform is not None:
|
||||||
|
next_meta['platform'] = update.platform
|
||||||
|
|
||||||
|
access_token.meta = next_meta
|
||||||
|
access_token.save()
|
||||||
|
|
||||||
|
access_token.refresh_from_db()
|
||||||
|
|
||||||
|
return access_token
|
||||||
|
|||||||
@@ -1,10 +1,62 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from bthlabs_jsonrpc_core import register_method
|
from bthlabs_jsonrpc_core import register_method
|
||||||
|
from django import db
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
|
||||||
|
from hotpocket_soa.dto.accounts import AccessTokenMetaUpdateIn
|
||||||
|
from hotpocket_soa.services import AccessTokensService
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@register_method('accounts.auth.check')
|
@register_method('accounts.auth.check')
|
||||||
def check(request: HttpRequest) -> bool:
|
def check(request: HttpRequest) -> bool:
|
||||||
return request.user.is_anonymous is False
|
return request.user.is_anonymous is False
|
||||||
|
|
||||||
|
|
||||||
|
@register_method('accounts.auth.check_access_token')
|
||||||
|
def check_access_token(request: HttpRequest,
|
||||||
|
access_token: str,
|
||||||
|
meta: dict | None = None,
|
||||||
|
) -> bool:
|
||||||
|
result = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
access_tokens_service = AccessTokensService()
|
||||||
|
|
||||||
|
with db.transaction.atomic():
|
||||||
|
access_token_object = access_tokens_service.get_by_key(
|
||||||
|
account_uuid=request.user.pk,
|
||||||
|
key=access_token,
|
||||||
|
)
|
||||||
|
|
||||||
|
meta_update = AccessTokenMetaUpdateIn.model_validate(
|
||||||
|
(meta or {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = access_tokens_service.update_meta(
|
||||||
|
access_token=access_token_object,
|
||||||
|
update=meta_update,
|
||||||
|
)
|
||||||
|
except AccessTokensService.AccessTokenNotFound as exception:
|
||||||
|
LOGGER.error(
|
||||||
|
'Access Token not found: account_uuid=`%s` key=`%s`',
|
||||||
|
request.user.pk,
|
||||||
|
access_token,
|
||||||
|
exc_info=exception,
|
||||||
|
)
|
||||||
|
result = False
|
||||||
|
except AccessTokensService.AccessTokenAccessDenied as exception:
|
||||||
|
LOGGER.error(
|
||||||
|
'Access Token access denied: account_uuid=`%s` key=`%s`',
|
||||||
|
request.user.pk,
|
||||||
|
access_token,
|
||||||
|
exc_info=exception,
|
||||||
|
)
|
||||||
|
result = False
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -12,24 +12,32 @@ def manifest_json(request: HttpRequest) -> JsonResponse:
|
|||||||
result = {
|
result = {
|
||||||
'name': settings.SITE_TITLE,
|
'name': settings.SITE_TITLE,
|
||||||
'short_name': settings.SITE_SHORT_TITLE,
|
'short_name': settings.SITE_SHORT_TITLE,
|
||||||
'start_url': reverse('ui.associations.browse'),
|
'start_url': request.build_absolute_uri(
|
||||||
|
reverse('ui.associations.browse'),
|
||||||
|
),
|
||||||
'display': 'standalone',
|
'display': 'standalone',
|
||||||
'background_color': '#212529',
|
'background_color': '#212529',
|
||||||
'theme_color': '#2b3035',
|
'theme_color': '#2b3035',
|
||||||
'icons': [
|
'icons': [
|
||||||
{
|
{
|
||||||
'src': static('ui/img/icon-192.png'),
|
'src': request.build_absolute_uri(
|
||||||
|
static('ui/img/icon-192.png'),
|
||||||
|
),
|
||||||
'sizes': '192x192',
|
'sizes': '192x192',
|
||||||
'type': 'image/png',
|
'type': 'image/png',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'src': static('ui/img/icon-512.png'),
|
'src': request.build_absolute_uri(
|
||||||
|
static('ui/img/icon-512.png'),
|
||||||
|
),
|
||||||
'sizes': '512x512',
|
'sizes': '512x512',
|
||||||
'type': 'image/png',
|
'type': 'image/png',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'share_target': {
|
'share_target': {
|
||||||
'action': reverse('ui.integrations.android.share_sheet'),
|
'action': request.build_absolute_uri(
|
||||||
|
reverse('ui.integrations.android.share_sheet'),
|
||||||
|
),
|
||||||
'method': 'POST',
|
'method': 'POST',
|
||||||
'enctype': 'multipart/form-data',
|
'enctype': 'multipart/form-data',
|
||||||
'params': {
|
'params': {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ cat <<EOF
|
|||||||
|_|
|
|_|
|
||||||
production
|
production
|
||||||
|
|
||||||
HotPocket v25.9.8 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/)
|
HotPocket v25.9.12 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/)
|
||||||
Copyright 2025-present by BTHLabs. All rights reserved. (https://bthlabs.pl/)
|
Copyright 2025-present by BTHLabs. All rights reserved. (https://bthlabs.pl/)
|
||||||
Licensed under Apache-2.0
|
Licensed under Apache-2.0
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hotpocket-backend",
|
"name": "hotpocket-backend",
|
||||||
"version": "25.9.8",
|
"version": "25.9.12",
|
||||||
"description": "HotPocket Backend",
|
"description": "HotPocket Backend",
|
||||||
"main": "hotpocket_backend/apps/frontend/src/index.js",
|
"main": "hotpocket_backend/apps/frontend/src/index.js",
|
||||||
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "hotpocket-backend"
|
name = "hotpocket-backend"
|
||||||
version = "25.9.8"
|
version = "25.9.12"
|
||||||
description = "HotPocket Backend"
|
description = "HotPocket Backend"
|
||||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from hotpocket_backend.apps.accounts.models import AccessToken
|
|||||||
def AccessTokenMetaFactory() -> dict:
|
def AccessTokenMetaFactory() -> dict:
|
||||||
return {
|
return {
|
||||||
'platform': 'MacIntel',
|
'platform': 'MacIntel',
|
||||||
'version': '1987.10.03',
|
'version': '1985.12.12',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ class AccessTokenFactory(factory.django.DjangoModelFactory):
|
|||||||
account_uuid = None
|
account_uuid = None
|
||||||
key = factory.LazyFunction(lambda: str(uuid.uuid4()))
|
key = factory.LazyFunction(lambda: str(uuid.uuid4()))
|
||||||
origin = factory.LazyFunction(
|
origin = factory.LazyFunction(
|
||||||
lambda: f'safari-web-extension//{uuid.uuid4()}',
|
lambda: f'safari-web-extension://{uuid.uuid4()}',
|
||||||
)
|
)
|
||||||
meta = factory.LazyFunction(AccessTokenMetaFactory)
|
meta = factory.LazyFunction(AccessTokenMetaFactory)
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from .access_token import * # noqa: F401,F403
|
from .access_token import * # noqa: F401,F403
|
||||||
from .account import * # noqa: F401,F403
|
from .account import * # noqa: F401,F403
|
||||||
|
from .apps import * # noqa: F401,F403
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# type: ignore
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def safari_extension_origin():
|
||||||
|
return f'safari-web-extension://{uuid.uuid4()}'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def safari_extension_meta():
|
||||||
|
return {
|
||||||
|
'platform': 'MacIntel',
|
||||||
|
'version': '1987.10.03',
|
||||||
|
}
|
||||||
@@ -24,15 +24,28 @@ class AccessTokensTestingService:
|
|||||||
assert access_token.updated_at is not None
|
assert access_token.updated_at is not None
|
||||||
|
|
||||||
def assert_deleted(self, *, pk: uuid.UUID, reference: typing.Any = None):
|
def assert_deleted(self, *, pk: uuid.UUID, reference: typing.Any = None):
|
||||||
association = AccessToken.objects.get(pk=pk)
|
access_token = AccessToken.objects.get(pk=pk)
|
||||||
assert association.deleted_at is not None
|
assert access_token.deleted_at is not None
|
||||||
|
|
||||||
if reference is not None:
|
if reference is not None:
|
||||||
assert association.updated_at > reference.updated_at
|
assert access_token.updated_at > reference.updated_at
|
||||||
|
|
||||||
def assert_not_deleted(self, *, pk: uuid.UUID, reference: typing.Any = None):
|
def assert_not_deleted(self, *, pk: uuid.UUID, reference: typing.Any = None):
|
||||||
association = AccessToken.objects.get(pk=pk)
|
access_token = AccessToken.objects.get(pk=pk)
|
||||||
assert association.deleted_at is None
|
assert access_token.deleted_at is None
|
||||||
|
|
||||||
if reference is not None:
|
if reference is not None:
|
||||||
assert association.updated_at == reference.updated_at
|
assert access_token.updated_at == reference.updated_at
|
||||||
|
|
||||||
|
def assert_meta_updated(self, *, pk: uuid.UUID, meta_update: dict, reference: typing.Any = None):
|
||||||
|
access_token = AccessToken.objects.get(pk=pk)
|
||||||
|
|
||||||
|
if len(meta_update) > 0 and reference is not None:
|
||||||
|
expected_meta = {
|
||||||
|
**reference.meta,
|
||||||
|
**meta_update,
|
||||||
|
}
|
||||||
|
assert access_token.meta == expected_meta
|
||||||
|
|
||||||
|
if reference is not None:
|
||||||
|
assert access_token.updated_at > reference.updated_at
|
||||||
|
|||||||
0
services/backend/tests/ui/views/meta/__init__.py
Normal file
0
services/backend/tests/ui/views/meta/__init__.py
Normal file
24
services/backend/tests/ui/views/meta/test_manifest_json.py
Normal file
24
services/backend/tests/ui/views/meta/test_manifest_json.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# type: ignore
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import http
|
||||||
|
|
||||||
|
from django.test import Client
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
def test_ok(client: Client, settings):
|
||||||
|
# When
|
||||||
|
result = client.get(reverse('ui.meta.manifest_json'))
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.OK
|
||||||
|
|
||||||
|
payload = result.json()
|
||||||
|
assert payload['name'] == settings.SITE_TITLE
|
||||||
|
assert payload['short_name'] == settings.SITE_SHORT_TITLE
|
||||||
|
assert payload['start_url'] == f"http://testserver{reverse('ui.associations.browse')}"
|
||||||
|
assert payload['share_target']['action'] == (
|
||||||
|
f"http://testserver{reverse('ui.integrations.android.share_sheet')}"
|
||||||
|
)
|
||||||
@@ -14,29 +14,16 @@ from hotpocket_backend_testing.services.accounts import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def origin():
|
|
||||||
return f'safari-web-extension://{uuid.uuid4()}'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def auth_key():
|
def auth_key():
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def meta():
|
def call(rpc_call_factory, auth_key, safari_extension_meta):
|
||||||
return {
|
|
||||||
'platform': 'MacIntel',
|
|
||||||
'version': '1987.10.03',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def call(rpc_call_factory, auth_key, meta):
|
|
||||||
return rpc_call_factory(
|
return rpc_call_factory(
|
||||||
'accounts.access_tokens.create',
|
'accounts.access_tokens.create',
|
||||||
[auth_key, meta],
|
[auth_key, safari_extension_meta],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +31,9 @@ def call(rpc_call_factory, auth_key, meta):
|
|||||||
def test_ok(authenticated_client: Client,
|
def test_ok(authenticated_client: Client,
|
||||||
auth_key,
|
auth_key,
|
||||||
call,
|
call,
|
||||||
origin,
|
safari_extension_origin,
|
||||||
account,
|
account,
|
||||||
meta,
|
safari_extension_meta,
|
||||||
):
|
):
|
||||||
# Given
|
# Given
|
||||||
session = authenticated_client.session
|
session = authenticated_client.session
|
||||||
@@ -59,7 +46,7 @@ def test_ok(authenticated_client: Client,
|
|||||||
data=call,
|
data=call,
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
headers={
|
headers={
|
||||||
'Origin': origin,
|
'Origin': safari_extension_origin,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,8 +59,8 @@ def test_ok(authenticated_client: Client,
|
|||||||
AccessTokensTestingService().assert_created(
|
AccessTokensTestingService().assert_created(
|
||||||
key=call_result['result'],
|
key=call_result['result'],
|
||||||
account_uuid=account.pk,
|
account_uuid=account.pk,
|
||||||
origin=origin,
|
origin=safari_extension_origin,
|
||||||
meta=meta,
|
meta=safari_extension_meta,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert 'extension_auth_key' not in authenticated_client.session
|
assert 'extension_auth_key' not in authenticated_client.session
|
||||||
@@ -82,7 +69,7 @@ def test_ok(authenticated_client: Client,
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_auth_key_missing(authenticated_client: Client,
|
def test_auth_key_missing(authenticated_client: Client,
|
||||||
call,
|
call,
|
||||||
origin,
|
safari_extension_origin,
|
||||||
):
|
):
|
||||||
# When
|
# When
|
||||||
result = authenticated_client.post(
|
result = authenticated_client.post(
|
||||||
@@ -90,7 +77,7 @@ def test_auth_key_missing(authenticated_client: Client,
|
|||||||
data=call,
|
data=call,
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
headers={
|
headers={
|
||||||
'Origin': origin,
|
'Origin': safari_extension_origin,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,7 +92,7 @@ def test_auth_key_missing(authenticated_client: Client,
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_auth_key_mismatch(authenticated_client: Client,
|
def test_auth_key_mismatch(authenticated_client: Client,
|
||||||
call,
|
call,
|
||||||
origin,
|
safari_extension_origin,
|
||||||
):
|
):
|
||||||
# Given
|
# Given
|
||||||
session = authenticated_client.session
|
session = authenticated_client.session
|
||||||
@@ -118,7 +105,7 @@ def test_auth_key_mismatch(authenticated_client: Client,
|
|||||||
data=call,
|
data=call,
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
headers={
|
headers={
|
||||||
'Origin': origin,
|
'Origin': safari_extension_origin,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# type: ignore
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import http
|
||||||
|
|
||||||
|
from django.test import Client
|
||||||
|
from django.urls import reverse
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from hotpocket_backend_testing.services.accounts import (
|
||||||
|
AccessTokensTestingService,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def call_factory(request: pytest.FixtureRequest, rpc_call_factory):
|
||||||
|
default_access_token = request.getfixturevalue('access_token_out')
|
||||||
|
default_meta_update = request.getfixturevalue('safari_extension_meta')
|
||||||
|
|
||||||
|
def factory(access_token=None, meta_update=None):
|
||||||
|
return rpc_call_factory(
|
||||||
|
'accounts.auth.check_access_token',
|
||||||
|
[
|
||||||
|
(
|
||||||
|
access_token.key
|
||||||
|
if access_token is not None
|
||||||
|
else default_access_token.key
|
||||||
|
),
|
||||||
|
(
|
||||||
|
meta_update
|
||||||
|
if meta_update is not None
|
||||||
|
else default_meta_update
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
return factory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def call(call_factory):
|
||||||
|
return call_factory()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_ok(authenticated_client: Client,
|
||||||
|
call,
|
||||||
|
access_token_out,
|
||||||
|
safari_extension_meta,
|
||||||
|
):
|
||||||
|
# When
|
||||||
|
result = authenticated_client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.OK
|
||||||
|
|
||||||
|
call_result = result.json()
|
||||||
|
assert 'error' not in call_result
|
||||||
|
assert call_result['result'] is True
|
||||||
|
|
||||||
|
AccessTokensTestingService().assert_meta_updated(
|
||||||
|
pk=access_token_out.pk,
|
||||||
|
meta_update=safari_extension_meta,
|
||||||
|
reference=access_token_out,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'meta_keys_to_pop',
|
||||||
|
[
|
||||||
|
('platform',),
|
||||||
|
('version',),
|
||||||
|
('platform', 'version'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_ok_with_partial_meta_update(meta_keys_to_pop,
|
||||||
|
safari_extension_meta,
|
||||||
|
authenticated_client: Client,
|
||||||
|
call_factory,
|
||||||
|
access_token_out,
|
||||||
|
):
|
||||||
|
# Given
|
||||||
|
meta_update = {**safari_extension_meta}
|
||||||
|
for meta_key_to_pop in meta_keys_to_pop:
|
||||||
|
meta_update.pop(meta_key_to_pop)
|
||||||
|
|
||||||
|
call = call_factory(meta_update=meta_update)
|
||||||
|
|
||||||
|
# When
|
||||||
|
result = authenticated_client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.OK
|
||||||
|
|
||||||
|
call_result = result.json()
|
||||||
|
assert 'error' not in call_result
|
||||||
|
assert call_result['result'] is True
|
||||||
|
|
||||||
|
AccessTokensTestingService().assert_meta_updated(
|
||||||
|
pk=access_token_out.pk,
|
||||||
|
meta_update=meta_update,
|
||||||
|
reference=access_token_out,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_invalid_access_token(authenticated_client: Client,
|
||||||
|
call,
|
||||||
|
):
|
||||||
|
# Given
|
||||||
|
call['params'][0] = 'thisisntright'
|
||||||
|
|
||||||
|
# When
|
||||||
|
result = authenticated_client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.OK
|
||||||
|
|
||||||
|
call_result = result.json()
|
||||||
|
assert 'error' not in call_result
|
||||||
|
assert call_result['result'] is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_deleted_access_token(call_factory,
|
||||||
|
deleted_access_token_out,
|
||||||
|
authenticated_client: Client,
|
||||||
|
):
|
||||||
|
# Given
|
||||||
|
call = call_factory(access_token=deleted_access_token_out)
|
||||||
|
|
||||||
|
# When
|
||||||
|
result = authenticated_client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.OK
|
||||||
|
|
||||||
|
call_result = result.json()
|
||||||
|
assert 'error' not in call_result
|
||||||
|
assert call_result['result'] is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_other_account_access_token(call_factory,
|
||||||
|
other_account_access_token_out,
|
||||||
|
authenticated_client: Client,
|
||||||
|
):
|
||||||
|
# Given
|
||||||
|
call = call_factory(access_token=other_account_access_token_out)
|
||||||
|
|
||||||
|
# When
|
||||||
|
result = authenticated_client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
content_type='application/json',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.OK
|
||||||
|
|
||||||
|
call_result = result.json()
|
||||||
|
assert 'error' not in call_result
|
||||||
|
assert call_result['result'] is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_inactive_account(inactive_account_client: Client, call):
|
||||||
|
# When
|
||||||
|
result = inactive_account_client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.FORBIDDEN
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_anonymous(client: Client, call):
|
||||||
|
# When
|
||||||
|
result = client.post(
|
||||||
|
reverse('ui.rpc'),
|
||||||
|
data=call,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then
|
||||||
|
assert result.status_code == http.HTTPStatus.FORBIDDEN
|
||||||
12
services/dotcom/webroot/privacy.html
Normal file
12
services/dotcom/webroot/privacy.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>hotpocket.app</title>
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>SOON</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hotpocket-extension",
|
"name": "hotpocket-extension",
|
||||||
"version": "25.9.8",
|
"version": "25.9.12",
|
||||||
"description": "HotPocket Extension",
|
"description": "HotPocket Extension",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "hotpocket-extension"
|
name = "hotpocket-extension"
|
||||||
version = "25.9.8"
|
version = "25.9.12"
|
||||||
description = "HotPocket Extension"
|
description = "HotPocket Extension"
|
||||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ const manifestJsonOutputPlugin = () => {
|
|||||||
|
|
||||||
result.version = packageJSON.version;
|
result.version = packageJSON.version;
|
||||||
|
|
||||||
|
if (IS_PRODUCTION === false) {
|
||||||
|
result.name = 'HotPocket Development';
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.stringify(result, null, 2);
|
return JSON.stringify(result, null, 2);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,9 +29,12 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
|||||||
let result = null;
|
let result = null;
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
|
const effectiveURL = new URL(url);
|
||||||
|
effectiveURL.searchParams.append('method', call.method);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
url,
|
effectiveURL.toString(),
|
||||||
{
|
{
|
||||||
body: JSON.stringify(call),
|
body: JSON.stringify(call),
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@@ -78,6 +81,13 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
|||||||
return [result, error];
|
return [result, error];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAccessTokenMeta = () => {
|
||||||
|
return {
|
||||||
|
platform: navigator.platform,
|
||||||
|
version: HotPocketExtension.version,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const doSave = async (accessToken, tab) => {
|
const doSave = async (accessToken, tab) => {
|
||||||
const call = makeJSONRPCCall('saves.create', [tab.url]);
|
const call = makeJSONRPCCall('saves.create', [tab.url]);
|
||||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {accessToken});
|
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {accessToken});
|
||||||
@@ -97,10 +107,7 @@ const doCreateAndStoreAccessToken = async (authKey) => {
|
|||||||
'accounts.access_tokens.create',
|
'accounts.access_tokens.create',
|
||||||
[
|
[
|
||||||
authKey,
|
authKey,
|
||||||
{
|
getAccessTokenMeta(),
|
||||||
platform: navigator.platform,
|
|
||||||
version: HotPocketExtension.version,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -163,7 +170,10 @@ const doCheckAuth = async (accessToken) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const call = makeJSONRPCCall('accounts.auth.check');
|
const call = makeJSONRPCCall(
|
||||||
|
'accounts.auth.check_access_token',
|
||||||
|
[accessToken, getAccessTokenMeta()],
|
||||||
|
);
|
||||||
|
|
||||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {
|
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {
|
||||||
accessToken,
|
accessToken,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"name": "__MSG_extension_name__",
|
"name": "__MSG_extension_name__",
|
||||||
"description": "__MSG_extension_description__",
|
"description": "__MSG_extension_description__",
|
||||||
"version": "25.9.8",
|
"version": "25.9.12",
|
||||||
"icons": {
|
"icons": {
|
||||||
"48": "images/icon-48.png",
|
"48": "images/icon-48.png",
|
||||||
"64": "images/icon-64.png",
|
"64": "images/icon-64.png",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"scripts": [
|
"scripts": [
|
||||||
"background-bundle.js"
|
"background-bundle.js"
|
||||||
],
|
],
|
||||||
"type": "module"
|
"type": "module",
|
||||||
|
"persistent": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,3 +36,8 @@ class AccessTokenOut(ModelOut):
|
|||||||
class AccessTokensQuery(Query):
|
class AccessTokensQuery(Query):
|
||||||
account_uuid: uuid.UUID
|
account_uuid: uuid.UUID
|
||||||
before: uuid.UUID | None = pydantic.Field(default=None)
|
before: uuid.UUID | None = pydantic.Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
|
class AccessTokenMetaUpdateIn(pydantic.BaseModel):
|
||||||
|
version: str | None = None
|
||||||
|
platform: str | None = None
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import uuid
|
|||||||
from hotpocket_backend.apps.accounts.services import (
|
from hotpocket_backend.apps.accounts.services import (
|
||||||
AccessTokensService as BackendAccessTokensService,
|
AccessTokensService as BackendAccessTokensService,
|
||||||
)
|
)
|
||||||
from hotpocket_soa.dto.accounts import AccessTokenOut, AccessTokensQuery
|
from hotpocket_soa.dto.accounts import (
|
||||||
|
AccessTokenMetaUpdateIn,
|
||||||
|
AccessTokenOut,
|
||||||
|
AccessTokensQuery,
|
||||||
|
)
|
||||||
|
|
||||||
from .base import ProxyService, SOAError
|
from .base import ProxyService, SOAError
|
||||||
|
|
||||||
@@ -76,6 +80,33 @@ class AccessTokensService(ProxyService):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def get_by_key(self,
|
||||||
|
*,
|
||||||
|
account_uuid: uuid.UUID,
|
||||||
|
key: str,
|
||||||
|
) -> AccessTokenOut:
|
||||||
|
try:
|
||||||
|
result = AccessTokenOut.model_validate(
|
||||||
|
self.call(
|
||||||
|
self.backend_access_tokens_service,
|
||||||
|
'get_by_key',
|
||||||
|
key=key,
|
||||||
|
),
|
||||||
|
from_attributes=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.account_uuid != account_uuid:
|
||||||
|
raise self.AccessTokenAccessDenied(
|
||||||
|
f'account_uuid=`{account_uuid}` key=`{key}`',
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
except SOAError as exception:
|
||||||
|
if isinstance(exception.__cause__, BackendAccessTokensService.AccessTokenNotFound) is True:
|
||||||
|
raise self.AccessTokenNotFound(f'account_uuid=`{account_uuid}` pk=`{key}`') from exception
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
def search(self,
|
def search(self,
|
||||||
*,
|
*,
|
||||||
query: AccessTokensQuery,
|
query: AccessTokensQuery,
|
||||||
@@ -98,3 +129,18 @@ class AccessTokensService(ProxyService):
|
|||||||
'delete',
|
'delete',
|
||||||
pk=access_token.pk,
|
pk=access_token.pk,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_meta(self,
|
||||||
|
*,
|
||||||
|
access_token: AccessTokenOut,
|
||||||
|
update: AccessTokenMetaUpdateIn,
|
||||||
|
) -> AccessTokenOut:
|
||||||
|
return AccessTokenOut.model_validate(
|
||||||
|
self.call(
|
||||||
|
self.backend_access_tokens_service,
|
||||||
|
'update_meta',
|
||||||
|
pk=access_token.pk,
|
||||||
|
update=update,
|
||||||
|
),
|
||||||
|
from_attributes=True,
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user