Compare commits

...

15 Commits

Author SHA1 Message Date
168657bd14 Release v25.10.21
All checks were successful
CI / Checks (push) Successful in 1m46s
2025-10-21 20:27:10 +02:00
6d49db5081 BTHLABS-0000: hotpocket.work.bthlabs.net vhosts for dotcom 2025-10-21 20:25:19 +02:00
9a6ade0d96 BTHLABS-0000: AIO settings fixes 2025-10-21 20:24:38 +02:00
a6e9b55837 Release v25.10.20 2025-10-20 20:30:50 +02:00
356f6ad76f BTHLABS-0000: Tweaking icons
Reviewed-on: hotpocket/hotpocket#20
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-10-20 18:14:22 +00:00
fbdebec6c8 BTHLABS-0000: sandstone and sketchy Bootswatch themes 2025-10-17 13:14:21 +02:00
10fccc17f7 BTHLABS-0000: development workflow
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-10-17 06:49:10 +00:00
0cf7b27f89 BTHLABS-0000: Deps update (Oct 2025)
Featuring Poetry bump to 2.2.1 :)
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-10-15 04:16:27 +00:00
0ac2ca73ec Release v25.10.13
All checks were successful
CI / Checks (push) Successful in 3m58s
2025-10-13 21:46:18 +02:00
7b67a2f758 BTHLABS-62: Display progress in extension popup
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-10-13 18:48:00 +00:00
8b86145519 BTHLABS-61: Service layer refactoring
A journey to fix `ValidationError` in Pocket imports turned service
layer refactoring :D
2025-10-12 20:54:00 +02:00
ac7a8dd90e BTHLABS-0000: README.md fixes 2025-10-07 08:46:50 +02:00
6903b7f768 BTHLABS-0000: Nuked dotcom service
Moved to a separate repo
2025-10-07 08:45:10 +02:00
2e8b8d7330 BTHLABS-60: Appearance settings
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-10-07 04:42:58 +00:00
b4d5375954 BTHLABS-0000: Docker and CI tweaks
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-10-07 04:37:01 +00:00
160 changed files with 4065 additions and 1788 deletions

View File

@ -0,0 +1,26 @@
name: "Get Build Options"
description: "Sanitizies and unifies the environment into build options"
outputs:
short-sha:
description: "Shortened hash if the current commit"
build-arch:
description: "Docker-compatible representation of build arch"
build-platform:
description: "Docker-compatible representation of build platform"
runs:
using: "composite"
steps:
- name: "Compute Build Options"
shell: "bash"
run: |
set -x
SHORT_SHA="${GITHUB_SHA::8}"
BUILD_ARCH="amd64"
BUILD_PLATFORM="linux/amd64"
if [ "${RUNNER_ARCH}" = "ARM64" ];then
BUILD_ARCH="arm64"
BUILD_PLATFORM="linux/arm64"
fi
echo "short-sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "build-arch=$BUILD_ARCH" >> $GITHUB_OUTPUT
echo "build-platform=$BUILD_PLATFORM" >> $GITHUB_OUTPUT

View File

@ -0,0 +1,17 @@
name: "Get Run Info"
description: "Sanitizies and unifies the environment into run info"
inputs:
compose-project-base:
description: "Base for the Compose project"
required: true
outputs:
compose-project:
description: "Compose project name"
runs:
using: "composite"
steps:
- name: "Compute Run Info"
shell: "bash"
run: |
set -x
echo "compose-project=${{ inputs.compose-project-base }}-${GITHUB_RUN_NUMBER}" >> $GITHUB_OUTPUT

View File

@ -0,0 +1,27 @@
name: "Get Run Info"
description: "Sanitizies and unifies the environment into run info"
inputs:
service:
description: "The service to work on"
required: true
outputs:
version:
description: "Service version"
build-number:
description: "Build number"
runs:
using: "composite"
steps:
- name: "Compute Service Version"
shell: "bash"
run: |
set -x
if [[ ! -z "${GITHUB_HEAD_REF}" || "${GITHUB_REF_NAME}" = "development" ]]; then
VERSION="${GITHUB_SHA::8}"
BUILD="${GITHUB_RUN_NUMBER}"
else
VERSION="v$(grep -Po '(?<=^version\s=\s")[^"]+' services/${{ inputs.service }}/pyproject.toml)"
BUILD="01"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "build-number=$BUILD" >> $GITHUB_OUTPUT

View File

@ -0,0 +1,32 @@
name: "Set up k8s"
description: "Downloads and installs k8s tools"
inputs:
arch:
description: "Architecture"
required: true
kubectl-version:
description: "kubectl version to install"
required: false
default: "1.33.4"
kustomize-version:
description: "kustomize version to install"
required: false
default: "5.7.1"
runs:
using: "composite"
steps:
- name: "Install k8s tools"
shell: "bash"
run: |
set -x
mkdir -p /opt/k8s/bin /opt/k8s/etc /opt/k8s/src
wget -O /opt/k8s/src/kubectl "https://nexus.bthlabs.pl/repository/ops-tools/k8s/kubectl-${{ inputs.kubectl-version }}-linux-${{ inputs.arch }}"
chmod a+x /opt/k8s/src/kubectl
mv /opt/k8s/src/kubectl /opt/k8s/bin
wget -O /opt/k8s/src/kustomize "https://nexus.bthlabs.pl/repository/ops-tools/k8s/kustomize-${{ inputs.kustomize-version }}-linux-${{ inputs.arch }}"
chmod a+x /opt/k8s/src/kustomize
mv /opt/k8s/src/kustomize /opt/k8s/bin
rm -rf /opt/k8s/src/

View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e
set +x
set -o pipefail
cat >"./docker-compose-ci-${COMPOSE_PROJECT}.yaml" <<EOF
services:
postgres:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/postgres:15.13-${COMPOSE_PROJECT}"
keycloak:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/keycloak:22.0.3-${COMPOSE_PROJECT}"
rabbitmq:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/rabbitmq:3.10.8-${COMPOSE_PROJECT}"
apple-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-${COMPOSE_PROJECT}"
backend-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:ci-${COMPOSE_PROJECT}"
extension-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-${COMPOSE_PROJECT}"
packages-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/packages:ci-${COMPOSE_PROJECT}"
EOF

View File

@ -17,8 +17,24 @@ jobs:
steps: steps:
- name: "Checkout the code" - name: "Checkout the code"
uses: "actions/checkout@v2" uses: "actions/checkout@v2"
- name: "Get run info"
id: "get-run-info"
uses: "./.gitea/actions/get-run-info"
with:
compose-project-base: "${{ vars.COMPOSE_PROJECT_BASE }}"
- name: "Get build options"
id: "get-build-options"
uses: "./.gitea/actions/get-build-options"
- name: "Set up Docker Buildx" - name: "Set up Docker Buildx"
id: "setup-docker-buildx"
uses: "docker/setup-buildx-action@v3" uses: "docker/setup-buildx-action@v3"
with:
driver: "remote"
endpoint: "tcp://builder-01.bthlab:2375"
platforms: "linux/amd64"
append: |
- endpoint: "tcp://builder-mac-01.bthlab:2375"
platforms: "linux/arm64"
- name: "Build `postgres` image" - name: "Build `postgres` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -26,7 +42,10 @@ jobs:
context: "services/" context: "services/"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/postgres:15.13-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/postgres:15.13-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `keycloak` image" - name: "Build `keycloak` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -34,7 +53,10 @@ jobs:
context: "services/" context: "services/"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/keycloak:22.0.3-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/keycloak:22.0.3-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `rabbitmq` image" - name: "Build `rabbitmq` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -42,7 +64,10 @@ jobs:
context: "services/" context: "services/"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/rabbitmq:3.10.8-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/rabbitmq:3.10.8-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `backend-ci` image" - name: "Build `backend-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -51,7 +76,10 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:ci-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `packages-ci` image" - name: "Build `packages-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -60,7 +88,10 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/packages:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/packages:ci-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `extension-ci` image" - name: "Build `extension-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -69,23 +100,91 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `apple-ci` image"
uses: docker/build-push-action@v6
with:
file: "services/apple/Dockerfile"
context: "services/"
target: "ci"
push: false
load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-${{ steps.get-run-info.outputs.compose-project }}"
platforms: "${{ steps.get-build-options.outputs.build-platform }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Prepare the build"
id: "prepare"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
run: |
set -x
./.gitea/tools/render-docker-compose-ci.sh
- name: "Run `backend` checks" - name: "Run `backend` checks"
if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm backend-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
backend-ci inv ci
- name: "Run `packages` checks" - name: "Run `packages` checks"
if: always() if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm packages-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
packages-ci inv ci
- name: "Run `extension` checks" - name: "Run `extension` checks"
if: always() if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm extension-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
extension-ci inv ci
- name: "Run `apple` checks"
if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
run: |
set -x
docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
apple-ci inv ci
- name: "Clean up" - name: "Clean up"
if: always() if: always()
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml down --volumes docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
down --volumes --rmi all || true
rm -f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" || true

View File

@ -0,0 +1,163 @@
name: "Deploy to development"
on:
push:
branches:
- "development"
jobs:
build-deployment-images:
name: "Build deployment images"
runs-on: "ubuntu-latest"
steps:
- name: "Checkout the code"
uses: "actions/checkout@v2"
- name: "Get build options"
id: "get-build-options"
uses: "./.gitea/actions/get-build-options"
- name: "Get `backend` version"
id: "get-backend-version"
uses: "./.gitea/actions/get-service-version"
with:
service: "backend"
- name: "Import Secrets"
id: "import-secrets"
uses: "hashicorp/vault-action@v2"
with:
url: "https://vault.bthlabs.pl/"
method: "approle"
roleId: "${{ secrets.VAULT_ROLE_ID }}"
secretId: "${{ secrets.VAULT_SECRET_ID }}"
secrets: |
gitea/data/docker-hosted.nexus.bthlabs.pl username | DOCKER_USERNAME ;
gitea/data/docker-hosted.nexus.bthlabs.pl password | DOCKER_PASSWORD
- name: "Set up Docker Buildx"
id: "setup-docker-buildx"
uses: "docker/setup-buildx-action@v3"
with:
driver: "remote"
endpoint: "tcp://builder-01.bthlab:2375"
platforms: "linux/amd64"
append: |
- endpoint: "tcp://builder-mac-01.bthlab:2375"
platforms: "linux/arm64"
- name: "Login to Docket Registry"
uses: "docker/login-action@v3"
with:
registry: "docker-hosted.nexus.bthlabs.pl"
username: "${{ steps.import-secrets.outputs.DOCKER_USERNAME }}"
password: "${{ steps.import-secrets.outputs.DOCKER_PASSWORD }}"
- name: "Build `backend-deployment` image"
env:
SHORT_SHA: "${{ steps.get-build-options.outputs.short-sha }}"
VERSION: "${{ steps.get-backend-version.outputs.version }}"
BUILD: "${{ steps.get-backend-version.outputs.build-number }}"
run: |
set -x
docker buildx build \
--cache-from "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket" \
--cache-to "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max" \
--push \
--platform linux/amd64,linux/arm64 \
--build-arg IMAGE_ID="deployment.${SHORT_SHA}" \
-f services/backend/Dockerfile \
--target deployment \
-t "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-${VERSION}-${BUILD}" \
services/
- name: "Build `backend-aio` image"
env:
SHORT_SHA: "${{ steps.get-build-options.outputs.short-sha }}"
VERSION: "${{ steps.get-backend-version.outputs.version }}"
BUILD: "${{ steps.get-backend-version.outputs.build-number }}"
run: |
set -x
docker buildx build \
--cache-from "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket" \
--cache-to "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max" \
--push \
--platform linux/amd64,linux/arm64 \
--build-arg IMAGE_ID="aio.${SHORT_SHA}" \
-f services/backend/Dockerfile \
--target aio \
-t "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-${VERSION}-${BUILD}" \
services/
deploy:
name: "Deploy"
runs-on: "ubuntu-latest"
needs:
- "build-deployment-images"
env:
KUBERNETES_NAMESPACE: "hotpocket-development"
KUBERNETES_CLUSTER: "k8s.bthlab"
steps:
- name: "Checkout the code"
uses: "actions/checkout@v2"
- name: "Get run info"
id: "get-run-info"
uses: "./.gitea/actions/get-run-info"
with:
compose-project-base: "${{ vars.COMPOSE_PROJECT_BASE }}"
- name: "Get build options"
id: "get-build-options"
uses: "./.gitea/actions/get-build-options"
- name: "Get `backend` version"
id: "get-backend-version"
uses: "./.gitea/actions/get-service-version"
with:
service: "backend"
- name: "Setup k8s"
uses: "./.gitea/actions/setup-k8s"
with:
arch: "${{ steps.get-build-options.outputs.build-arch }}"
- name: "Import Secrets"
id: "import-secrets"
uses: "hashicorp/vault-action@v2"
with:
url: "https://vault.bthlabs.pl/"
method: "approle"
roleId: "${{ secrets.VAULT_ROLE_ID }}"
secretId: "${{ secrets.VAULT_SECRET_ID }}"
secrets: |
gitea/data/k8s.bthlab config | KUBECONFIG_PAYLOAD
- name: "Set up kubeconfig"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
KUBECONFIG_PAYLOAD: "${{ steps.import-secrets.outputs.KUBECONFIG_PAYLOAD }}"
run: |
set -x
echo ${KUBECONFIG_PAYLOAD} | base64 -d >"/opt/k8s/etc/kubeconfig"
export KUBECONFIG="/opt/k8s/etc/kubeconfig"
/opt/k8s/bin/kubectl config use-context ${KUBERNETES_CLUSTER}
/opt/k8s/bin/kubectl get node
- name: "Run `backend` Django migrations"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
BACKEND_TAG: "deployment-${{ steps.get-backend-version.outputs.version }}-${{ steps.get-backend-version.outputs.build-number }}"
run: |
set -x
(
cd deployment/hotpocket.bthlab ;
export KUBECONFIG="/opt/k8s/etc/kubeconfig" ;
/opt/k8s/bin/kubectl config use-context ${KUBERNETES_CLUSTER} ;
/opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} set image cronjobs/backend-job-migrations migrations=docker-hosted.nexus.bthlabs.pl/hotpocket/backend:${BACKEND_TAG} ;
/opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} delete jobs --ignore-not-found=true backend-job-migrations ;
/opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} create job backend-job-migrations --from=cronjob/backend-job-migrations ;
/opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} wait --for=condition=complete --timeout=300s job/backend-job-migrations
)
- name: "Deploy"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}"
BACKEND_TAG: "deployment-${{ steps.get-backend-version.outputs.version }}-${{ steps.get-backend-version.outputs.build-number }}"
run: |
set -x
(
cd deployment/hotpocket.bthlab ;
export KUBECONFIG="/opt/k8s/etc/kubeconfig" ;
/opt/k8s/bin/kubectl config use-context ${KUBERNETES_CLUSTER} ;
/opt/k8s/bin/kustomize edit set image hotpocket-backend=docker-hosted.nexus.bthlabs.pl/hotpocket/backend:${BACKEND_TAG} ;
/opt/k8s/bin/kustomize build . | /opt/k8s/bin/kubectl apply -f -
)

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
.ci/
.envrc* .envrc*
.ipythonhome/ .ipythonhome/
/docker-compose-ci-*.yaml

View File

@ -86,3 +86,9 @@ Licensed under terms of the MIT License
Pepper Hot Solid icon Pepper Hot Solid icon
Copyright (c) Icons8 Copyright (c) Icons8
Licensed under terms of the MIT License Licensed under terms of the MIT License
Spinner Loader CSS from https://css-loaders.com/
cosmo, sandstone, sketchy and solar Bootswatch Themes
Copyright 2012-2025 Thomas Park
Licensed under terms of the MIT License

View File

@ -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.10.4-01 docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.10.21-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
@ -76,8 +76,7 @@ credentials. The Web app will be reachable at `http://127.0.0.1:8000/`.
The admin will be reachable at `http://127.0.0.1:8000/admin/`. The admin will be reachable at `http://127.0.0.1:8000/admin/`.
The `DJANGO_SETTINGS_MODULE` environment variable defaults to The `DJANGO_SETTINGS_MODULE` environment variable defaults to
`hotpocket_backend.settings.deployment.webapp`. This should be set to `hotpocket_backend.settings.deployment.aio`.
`hotpocket_backend.settings.deployment.admin` in the Admin container.
**NOTE:** The command above specifies wildly insecure `SECRET_KEY` which is **NOTE:** The command above specifies wildly insecure `SECRET_KEY` which is
used among other things to secure the session cookie. Please *please* used among other things to secure the session cookie. Please *please*
@ -94,7 +93,8 @@ backend etc. The final deployment will require services for at least the Web
app, the Celery worker and Celery Beat. Admin is optional. app, the Celery worker and Celery Beat. Admin is optional.
The `DJANGO_SETTINGS_MODULE` environment variable defaults to The `DJANGO_SETTINGS_MODULE` environment variable defaults to
`hotpocket_backend.settings.deployment.aio`. `hotpocket_backend.settings.deployment.webapp`. This should be set to
`hotpocket_backend.settings.deployment.admin` in the Admin container.
The `deployment/fullstack/docker-compose.yaml` file can be used as a The `deployment/fullstack/docker-compose.yaml` file can be used as a
starting point for full-stack deployments. starting point for full-stack deployments.

View File

@ -1,6 +1,6 @@
services: services:
backend: backend:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.10.4-01" image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.10.21-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"

View File

@ -8,7 +8,7 @@ x-backend-environment: &x-backend-environment
services: services:
webapp: webapp:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.10.4-01" image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.10.21-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.10.4-01" image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.10.21-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.10.4-01" image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.10.21-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.10.4-01" image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.10.21-01"
command: command:
- "/srv/venv/bin/celery" - "/srv/venv/bin/celery"
- "-A" - "-A"

View File

@ -0,0 +1,5 @@
DJANGO_SETTINGS_MODULE=hotpocket_backend.settings.deployment.admin
HOTPOCKET_BACKEND_GUNICORN_WORKERS=2
HOTPOCKET_BACKEND_APP=admin
HOTPOCKET_BACKEND_SECRET_KEY=thisissecret
HOTPOCKET_BACKEND_ALLOWED_HOSTS=thisissecret

View File

@ -0,0 +1,8 @@
HOTPOCKET_BACKEND_ENV=deployment
HOTPOCKET_BACKEND_DATABASE_NAME=hotpocket_development_backend
HOTPOCKET_BACKEND_DATABASE_USER=thisissecret
HOTPOCKET_BACKEND_DATABASE_PASSWORD=thisissecret
HOTPOCKET_BACKEND_DATABASE_HOST=databases.bthlab
HOTPOCKET_BACKEND_CELERY_BROKER_URL=thisissecret
HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND=thisissecret
HOTPOCKET_BACKEND_MODEL_AUTH_IS_DISABLED=false

View File

@ -0,0 +1,7 @@
DJANGO_SETTINGS_MODULE=hotpocket_backend.settings.deployment.webapp
HOTPOCKET_BACKEND_GUNICORN_WORKERS=2
HOTPOCKET_BACKEND_APP=webapp
HOTPOCKET_BACKEND_SECRET_KEY=thisissecret
HOTPOCKET_BACKEND_ALLOWED_HOSTS=thisissecret
HOTPOCKET_BACKEND_SAVES_SAVE_ADAPTER=hotpocket_backend.apps.saves.adapters.postgres:PostgresSaveAdapter
HOTPOCKET_BACKEND_SAVES_ASSOCIATION_ADAPTER=hotpocket_backend.apps.saves.adapters.postgres:PostgresAssociationAdapter

View File

@ -0,0 +1,39 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- resources/namespace.yaml
- resources/volumes.yaml
- resources/backend/job-migrations.yaml
- resources/backend/webapp.yaml
- resources/backend/webapp-service.yaml
- resources/backend/webapp-ingress.yaml
- resources/backend/admin.yaml
- resources/backend/admin-service.yaml
- resources/backend/admin-ingress.yaml
- resources/backend/celery-worker.yaml
- resources/backend/celery-beat.yaml
configMapGenerator:
- behavior: create
namespace: hotpocket-development
envs:
- configs/backend/base
name: backend-base-config
- behavior: create
namespace: hotpocket-development
envs:
- configs/backend/webapp
name: backend-webapp-config
- behavior: create
namespace: hotpocket-development
envs:
- configs/backend/admin
name: backend-admin-config
patches: []
images:
- name: hotpocket-backend
newName: docker-hosted.nexus.bthlabs.pl/hotpocket/backend
newTag: deployment-v25.10.4-01

View File

@ -0,0 +1,19 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-admin-ingress
namespace: hotpocket-development
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: "web"
spec:
rules:
- host: admin.hotpocket.bthlab.bthlabs.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-admin-service
port:
name: http

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: backend-admin-service
namespace: hotpocket-development
spec:
type: ClusterIP
selector:
app.kubernetes.io/app: backend-admin
ports:
- name: http
protocol: TCP
port: 8000
targetPort: http

View File

@ -0,0 +1,101 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-admin
namespace: hotpocket-development
labels:
app.kubernetes.io/app: backend-admin
spec:
minReadySeconds: 30
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
app.kubernetes.io/app: backend-admin
template:
metadata:
labels:
app.kubernetes.io/app: backend-admin
spec:
containers:
- name: app
image: hotpocket-backend:latest
command:
- "/srv/venv/bin/gunicorn"
- "-c"
- "/srv/lib/gunicorn.conf.py"
- "hotpocket_backend.wsgi:application"
envFrom:
- configMapRef:
name: backend-base-config
- configMapRef:
name: backend-admin-config
env:
- name: HOTPOCKET_BACKEND_SECRET_KEY
valueFrom:
secretKeyRef:
name: backend-admin
key: secret_key
- name: HOTPOCKET_BACKEND_ALLOWED_HOSTS
valueFrom:
secretKeyRef:
name: backend-admin
key: allowed_hosts
- name: HOTPOCKET_BACKEND_DATABASE_USER
valueFrom:
secretKeyRef:
name: backend-postgres
key: username
- name: HOTPOCKET_BACKEND_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: backend-postgres
key: password
- name: HOTPOCKET_BACKEND_CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: backend-celery
key: broker_url
- name: HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND
valueFrom:
secretKeyRef:
name: backend-celery
key: result_backend
ports:
- containerPort: 8000
name: http
protocol: TCP
- containerPort: 8001
name: healthcheck
protocol: TCP
livenessProbe:
httpGet:
path: "/"
port: 8001
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: "/"
port: 8001
initialDelaySeconds: 2
periodSeconds: 5
volumeMounts:
- mountPath: /dev/shm
name: shm
- mountPath: /srv/run
name: backend-admin-srv-run
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- name: shm
emptyDir:
medium: Memory
- name: backend-admin-srv-run
emptyDir: {}

View File

@ -0,0 +1,85 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: backend-celery-beat
namespace: hotpocket-development
labels:
app.kubernetes.io/app: backend-celery-beat
spec:
minReadySeconds: 30
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app.kubernetes.io/app: backend-celery-beat
template:
metadata:
labels:
app.kubernetes.io/app: backend-celery-beat
spec:
containers:
- name: app
image: hotpocket-backend:latest
command:
- "/srv/venv/bin/celery"
- "-A"
- "hotpocket_backend.celery:app"
- "beat"
- "-l"
- "INFO"
- "-s"
- "/srv/run/celery-beat-schedule"
envFrom:
- configMapRef:
name: backend-base-config
- configMapRef:
name: backend-webapp-config
env:
- name: HOTPOCKET_BACKEND_SECRET_KEY
valueFrom:
secretKeyRef:
name: backend-webapp
key: secret_key
- name: HOTPOCKET_BACKEND_ALLOWED_HOSTS
valueFrom:
secretKeyRef:
name: backend-webapp
key: allowed_hosts
- name: HOTPOCKET_BACKEND_DATABASE_USER
valueFrom:
secretKeyRef:
name: backend-postgres
key: username
- name: HOTPOCKET_BACKEND_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: backend-postgres
key: password
- name: HOTPOCKET_BACKEND_CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: backend-celery
key: broker_url
- name: HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND
valueFrom:
secretKeyRef:
name: backend-celery
key: result_backend
volumeMounts:
- mountPath: /dev/shm
name: shm
- mountPath: /srv/run
name: backend-celery-beat-srv-run
- mountPath: /srv/uploads
name: backend-celery-beat-srv-uploads
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- name: shm
emptyDir:
medium: Memory
- name: backend-celery-beat-srv-run
persistentVolumeClaim:
claimName: backend-celery-beat-run
- name: backend-celery-beat-srv-uploads
emptyDir: {}

View File

@ -0,0 +1,93 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-celery-worker
namespace: hotpocket-development
labels:
app.kubernetes.io/app: backend-celery-worker
spec:
minReadySeconds: 30
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
app.kubernetes.io/app: backend-celery-worker
template:
metadata:
labels:
app.kubernetes.io/app: backend-celery-worker
spec:
containers:
- name: app
image: hotpocket-backend:latest
command:
- "/srv/venv/bin/celery"
- "-A"
- "hotpocket_backend.celery:app"
- "worker"
- "-l"
- "INFO"
- "-Q"
- "celery,webapp"
- "-c"
- "2"
envFrom:
- configMapRef:
name: backend-base-config
- configMapRef:
name: backend-webapp-config
env:
- name: HOTPOCKET_BACKEND_SECRET_KEY
valueFrom:
secretKeyRef:
name: backend-webapp
key: secret_key
- name: HOTPOCKET_BACKEND_ALLOWED_HOSTS
valueFrom:
secretKeyRef:
name: backend-webapp
key: allowed_hosts
- name: HOTPOCKET_BACKEND_DATABASE_USER
valueFrom:
secretKeyRef:
name: backend-postgres
key: username
- name: HOTPOCKET_BACKEND_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: backend-postgres
key: password
- name: HOTPOCKET_BACKEND_CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: backend-celery
key: broker_url
- name: HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND
valueFrom:
secretKeyRef:
name: backend-celery
key: result_backend
volumeMounts:
- mountPath: /dev/shm
name: shm
- mountPath: /srv/run
name: backend-celery-worker-srv-run
- mountPath: /srv/uploads
name: backend-celery-worker-srv-uploads
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- name: shm
emptyDir:
medium: Memory
- name: backend-celery-worker-srv-run
emptyDir: {}
- name: backend-celery-worker-srv-uploads
persistentVolumeClaim:
claimName: backend-uploads

View File

@ -0,0 +1,80 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: backend-job-migrations
namespace: hotpocket-development
labels:
app.kubernetes.io/app: backend-job-migrations
spec:
concurrencyPolicy: "Forbid"
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
startingDeadlineSeconds: 180
schedule: "* * * * *"
suspend: true
jobTemplate:
spec:
backoffLimit: 1
completions: 1
parallelism: 1
template:
spec:
containers:
- name: migrations
image: hotpocket-backend:latest
command:
- "./manage.py"
- "migrate"
envFrom:
- configMapRef:
name: backend-base-config
- configMapRef:
name: backend-webapp-config
env:
- name: HOTPOCKET_BACKEND_SECRET_KEY
valueFrom:
secretKeyRef:
name: backend-webapp
key: secret_key
- name: HOTPOCKET_BACKEND_ALLOWED_HOSTS
valueFrom:
secretKeyRef:
name: backend-webapp
key: allowed_hosts
- name: HOTPOCKET_BACKEND_DATABASE_USER
valueFrom:
secretKeyRef:
name: backend-postgres
key: username
- name: HOTPOCKET_BACKEND_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: backend-postgres
key: password
- name: HOTPOCKET_BACKEND_CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: backend-celery
key: broker_url
- name: HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND
valueFrom:
secretKeyRef:
name: backend-celery
key: result_backend
volumeMounts:
- mountPath: /dev/shm
name: shm
- mountPath: /srv/run
name: backend-webapp-srv-run
- mountPath: /srv/uploads
name: backend-webapp-srv-uploads
dnsPolicy: ClusterFirst
restartPolicy: Never
volumes:
- name: shm
emptyDir:
medium: Memory
- name: backend-webapp-srv-run
emptyDir: {}
- name: backend-webapp-srv-uploads
emptyDir: {}

View File

@ -0,0 +1,19 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-webapp-ingress
namespace: hotpocket-development
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: "web"
spec:
rules:
- host: app.hotpocket.bthlab.bthlabs.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-webapp-service
port:
name: http

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: backend-webapp-service
namespace: hotpocket-development
spec:
type: ClusterIP
selector:
app.kubernetes.io/app: backend-webapp
ports:
- name: http
protocol: TCP
port: 8000
targetPort: http

View File

@ -0,0 +1,106 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-webapp
namespace: hotpocket-development
labels:
app.kubernetes.io/app: backend-webapp
spec:
minReadySeconds: 30
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
selector:
matchLabels:
app.kubernetes.io/app: backend-webapp
template:
metadata:
labels:
app.kubernetes.io/app: backend-webapp
spec:
containers:
- name: app
image: hotpocket-backend:latest
command:
- "/srv/venv/bin/gunicorn"
- "-c"
- "/srv/lib/gunicorn.conf.py"
- "hotpocket_backend.wsgi:application"
envFrom:
- configMapRef:
name: backend-base-config
- configMapRef:
name: backend-webapp-config
env:
- name: HOTPOCKET_BACKEND_SECRET_KEY
valueFrom:
secretKeyRef:
name: backend-webapp
key: secret_key
- name: HOTPOCKET_BACKEND_ALLOWED_HOSTS
valueFrom:
secretKeyRef:
name: backend-webapp
key: allowed_hosts
- name: HOTPOCKET_BACKEND_DATABASE_USER
valueFrom:
secretKeyRef:
name: backend-postgres
key: username
- name: HOTPOCKET_BACKEND_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: backend-postgres
key: password
- name: HOTPOCKET_BACKEND_CELERY_BROKER_URL
valueFrom:
secretKeyRef:
name: backend-celery
key: broker_url
- name: HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND
valueFrom:
secretKeyRef:
name: backend-celery
key: result_backend
ports:
- containerPort: 8000
name: http
protocol: TCP
- containerPort: 8001
name: healthcheck
protocol: TCP
livenessProbe:
httpGet:
path: "/"
port: 8001
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: "/"
port: 8001
initialDelaySeconds: 2
periodSeconds: 5
volumeMounts:
- mountPath: /dev/shm
name: shm
- mountPath: /srv/run
name: backend-webapp-srv-run
- mountPath: /srv/uploads
name: backend-webapp-srv-uploads
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- name: shm
emptyDir:
medium: Memory
- name: backend-webapp-srv-run
emptyDir: {}
- name: backend-webapp-srv-uploads
persistentVolumeClaim:
claimName: backend-uploads

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: hotpocket-development

View File

@ -0,0 +1,26 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backend-uploads
namespace: hotpocket-development
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: "1Gi"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: backend-celery-beat-run
namespace: hotpocket-development
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: "1Gi"

View File

@ -2,6 +2,7 @@
"group": { "group": {
"default": { "default": {
"targets": [ "targets": [
"apple-management",
"backend-management", "backend-management",
"caddy", "caddy",
"extension-management", "extension-management",
@ -13,6 +14,28 @@
} }
}, },
"target": { "target": {
"apple-management": {
"context": "services/",
"dockerfile": "apple/Dockerfile",
"tags": [
"docker-hosted.nexus.bthlabs.pl/hotpocket/apple:local"
],
"target": "development",
"output": [
"type=docker,load=true,push=false"
]
},
"apple-ci": {
"context": "services/",
"dockerfile": "apple/Dockerfile",
"tags": [
"docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-local"
],
"target": "ci",
"output": [
"type=docker,load=true,push=false"
]
},
"backend-management": { "backend-management": {
"context": "services/", "context": "services/",
"dockerfile": "backend/Dockerfile", "dockerfile": "backend/Dockerfile",

View File

@ -1,16 +1,17 @@
services: services:
postgres: postgres:
ports: [] ports: !override []
keycloak: keycloak:
command: "echo 'NOOP'" command: "echo 'NOOP'"
ports: [] ports: !override []
restart: "no" restart: "no"
rabbitmq: rabbitmq:
ports: [] ports: !override []
include: include:
- path: "./services/backend/docker-compose-ci.yaml" - path: "./services/backend/docker-compose-ci.yaml"
- path: "./services/packages/docker-compose-ci.yaml" - path: "./services/packages/docker-compose-ci.yaml"
- path: "./services/extension/docker-compose-ci.yaml" - path: "./services/extension/docker-compose-ci.yaml"
- path: "./services/apple/docker-compose-ci.yaml"

View File

@ -6,6 +6,7 @@ include:
- path: "./services/backend/docker-compose.yaml" - path: "./services/backend/docker-compose.yaml"
- path: "./services/packages/docker-compose.yaml" - path: "./services/packages/docker-compose.yaml"
- path: "./services/extension/docker-compose.yaml" - path: "./services/extension/docker-compose.yaml"
- path: "./services/apple/docker-compose.yaml"
volumes: {} volumes: {}

16
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. # This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
[[package]] [[package]]
name = "hotpocket-workspace-tools" name = "hotpocket-workspace-tools"
@ -6,11 +6,12 @@ version = "1.0.0.dev0"
description = "HotPocket Workspace Tools" description = "HotPocket Workspace Tools"
optional = false optional = false
python-versions = "^3.12" python-versions = "^3.12"
groups = ["main"]
files = [] files = []
develop = true develop = true
[package.dependencies] [package.dependencies]
invoke = "2.2.0" invoke = "2.2.1"
[package.source] [package.source]
type = "directory" type = "directory"
@ -18,16 +19,17 @@ url = "services/packages/workspace_tools"
[[package]] [[package]]
name = "invoke" name = "invoke"
version = "2.2.0" version = "2.2.1"
description = "Pythonic task execution" description = "Pythonic task execution"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["main"]
files = [ files = [
{file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}, {file = "invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8"},
{file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, {file = "invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707"},
] ]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.1"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "ec33c3b3ec0f988e333872bdd134c1adce0782e98512dd2484cb85009b3da6cb" content-hash = "a7028d4a0260c82012077d9cc4b324b0ef5ab8ed24aa283a51cf941ba09685a9"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "hotpocket-workspace" name = "hotpocket-workspace"
version = "25.10.4" version = "25.10.21"
description = "HotPocket Workspace" description = "HotPocket Workspace"
authors = ["Tomek Wójcik <contact@bthlabs.pl>"] authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
license = "Apache-2.0" license = "Apache-2.0"
@ -9,7 +9,6 @@ package-mode = false
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.12" python = "^3.12"
hotpocket-workspace-tools = {path = "services/packages/workspace_tools", develop = true} hotpocket-workspace-tools = {path = "services/packages/workspace_tools", develop = true}
invoke = "2.2.0"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

View File

@ -1,5 +1,8 @@
.mypy_cache/
.pytest_cache/
_tmp/ _tmp/
apple/ apple/build/
apple/DerivedData/
backend/node_modules/ backend/node_modules/
backend/ops/metal/ backend/ops/metal/
backend/hotpocket_backend/playground.py backend/hotpocket_backend/playground.py

19
services/apple/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
ARG APP_USER_UID=1000
ARG APP_USER_GID=1000
ARG IMAGE_ID=development.00000000
FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:build-node-20251014-01 AS development
ARG APP_USER_UID
ARG APP_USER_GID
ARG IMAGE_ID
# COPY --chown=$APP_USER_UID:$APP_USER_GID apple/ops/bin/*.sh /srv/bin/
VOLUME ["/srv/node_modules", "/srv/venv"]
FROM development AS ci
COPY --chown=$APP_USER_UID:$APP_USER_GID apple/ /srv/app/
COPY --chown=$APP_USER_UID:$APP_USER_GID packages/ /srv/packages/
COPY --chown=$APP_USER_UID:$APP_USER_GID tls/ /srv/tls/

View File

@ -713,7 +713,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements"; CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Share Extension)/Info.plist"; INFOPLIST_FILE = "iOS (Share Extension)/Info.plist";
@ -726,7 +726,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension; PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket"; PRODUCT_NAME = "Save to HotPocket";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -746,7 +746,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements"; CODE_SIGN_ENTITLEMENTS = "iOS (Share Extension)/iOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "iOS (Share Extension)/Info.plist"; INFOPLIST_FILE = "iOS (Share Extension)/Info.plist";
@ -759,7 +759,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension; PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket"; PRODUCT_NAME = "Save to HotPocket";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -779,7 +779,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
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";
@ -792,7 +792,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -814,7 +814,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
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";
@ -827,7 +827,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -853,7 +853,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "iOS (App)/HotPocket (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "iOS (App)/HotPocket (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
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";
@ -873,7 +873,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -899,7 +899,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "iOS (App)/HotPocket (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "iOS (App)/HotPocket (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
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";
@ -919,7 +919,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -945,7 +945,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
@ -960,7 +960,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -980,7 +980,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
@ -995,7 +995,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -1017,7 +1017,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -1033,7 +1033,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -1056,7 +1056,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -1072,7 +1072,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -1206,7 +1206,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -1220,7 +1220,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension; PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket"; PRODUCT_NAME = "Save to HotPocket";
REGISTER_APP_GROUPS = YES; REGISTER_APP_GROUPS = YES;
@ -1236,7 +1236,7 @@
CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements"; CODE_SIGN_ENTITLEMENTS = "macOS (Share Extension)/macOS (Share Extension).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2025100401; CURRENT_PROJECT_VERSION = 2025102101;
DEVELOPMENT_TEAM = 648728X64K; DEVELOPMENT_TEAM = 648728X64K;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -1250,7 +1250,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 25.10.4; MARKETING_VERSION = 25.10.21;
PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension; PRODUCT_BUNDLE_IDENTIFIER = pl.bthlabs.HotPocket.ShareExtension;
PRODUCT_NAME = "Save to HotPocket"; PRODUCT_NAME = "Save to HotPocket";
REGISTER_APP_GROUPS = YES; REGISTER_APP_GROUPS = YES;

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "icon-1024.png", "filename" : "icon-ios-light.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"
@ -13,7 +13,7 @@
"value" : "dark" "value" : "dark"
} }
], ],
"filename" : "icon-1024 1.png", "filename" : "icon-ios-dark.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"
@ -25,31 +25,31 @@
"value" : "tinted" "value" : "tinted"
} }
], ],
"filename" : "icon-1024 2.png", "filename" : "icon-ios-tinted.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"
}, },
{ {
"filename" : "icon-16.png", "filename" : "icon-mac-16.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "icon-32.png", "filename" : "icon-mac-32.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "icon-32 1.png", "filename" : "icon-mac-32 1.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "32x32" "size" : "32x32"
}, },
{ {
"filename" : "icon-64.png", "filename" : "icon-mac-64.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "32x32" "size" : "32x32"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,23 @@
services:
apple-ci:
build:
context: ".."
dockerfile: "apple/Dockerfile"
target: "development"
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-local"
command: "echo 'NOOP'"
environment:
PYTHONBREAKPOINT: "ipdb.set_trace"
HOTPOCKET_PACKAGES_ENV: "${HOTPOCKET_EXTENSION_ENV:-docker}"
# REQUESTS_CA_BUNDLE: "/srv/tls/requests_ca_bundle.pem"
RUN_POETRY_INSTALL: "true"
RUN_YARN_INSTALL: "false"
SETUP_BACKEND: "true"
SETUP_FRONTEND: "false"
volumes:
- "apple_venv:/srv/venv"
- "apple_node_modules:/srv/node_modules"
- "../tls:/srv/tls"
restart: "no"
stdin_open: true
tty: true

View File

@ -0,0 +1,29 @@
services:
apple-management:
build:
context: ".."
dockerfile: "apple/Dockerfile"
target: "development"
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:local"
command: "echo 'NOOP'"
environment: &apple-env
PYTHONBREAKPOINT: "ipdb.set_trace"
HOTPOCKET_EXTENSION_ENV: "${HOTPOCKET_EXTENSION_ENV:-docker}"
REQUESTS_CA_BUNDLE: "/srv/tls/requests_ca_bundle.pem"
RUN_POETRY_INSTALL: "true"
RUN_YARN_INSTALL: "false"
SETUP_BACKEND: "true"
SETUP_FRONTEND: "false"
volumes:
- "apple_venv:/srv/venv"
- "apple_node_modules:/srv/node_modules"
- ".:/srv/app"
- "../packages:/srv/packages"
- "../tls:/srv/tls"
restart: "no"
stdin_open: true
tty: true
volumes:
apple_venv:
apple_node_modules:

View File

View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. # This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
[[package]] [[package]]
name = "asttokens" name = "asttokens"
@ -6,6 +6,7 @@ version = "3.0.0"
description = "Annotate AST trees with source code positions" description = "Annotate AST trees with source code positions"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
{file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
@ -21,6 +22,8 @@ version = "0.4.6"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
markers = "sys_platform == \"win32\""
files = [ files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@ -32,6 +35,7 @@ version = "5.2.1"
description = "Decorators for Humans" description = "Decorators for Humans"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
{file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
@ -43,13 +47,14 @@ version = "2.2.1"
description = "Get the currently executing AST node of a frame, and other information" description = "Get the currently executing AST node of a frame, and other information"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"}, {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"},
{file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"}, {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"},
] ]
[package.extras] [package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
[[package]] [[package]]
name = "factory-boy" name = "factory-boy"
@ -57,6 +62,7 @@ version = "3.3.3"
description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc"}, {file = "factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc"},
{file = "factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03"}, {file = "factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03"},
@ -71,13 +77,14 @@ doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
[[package]] [[package]]
name = "faker" name = "faker"
version = "37.6.0" version = "37.11.0"
description = "Faker is a Python package that generates fake data for you." description = "Faker is a Python package that generates fake data for you."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"]
files = [ files = [
{file = "faker-37.6.0-py3-none-any.whl", hash = "sha256:3c5209b23d7049d596a51db5d76403a0ccfea6fc294ffa2ecfef6a8843b1e6a7"}, {file = "faker-37.11.0-py3-none-any.whl", hash = "sha256:1508d2da94dfd1e0087b36f386126d84f8583b3de19ac18e392a2831a6676c57"},
{file = "faker-37.6.0.tar.gz", hash = "sha256:0f8cc34f30095184adf87c3c24c45b38b33ad81c35ef6eb0a3118f301143012c"}, {file = "faker-37.11.0.tar.gz", hash = "sha256:22969803849ba0618be8eee2dd01d0d9e2cd3b75e6ff1a291fa9abcdb34da5e6"},
] ]
[package.dependencies] [package.dependencies]
@ -89,6 +96,7 @@ version = "7.3.0"
description = "the modular source code checker: pep8 pyflakes and co" description = "the modular source code checker: pep8 pyflakes and co"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"]
files = [ files = [
{file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"},
{file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"},
@ -105,6 +113,7 @@ version = "4.0.0"
description = "Flake8 lint for trailing commas." description = "Flake8 lint for trailing commas."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "flake8_commas-4.0.0-py3-none-any.whl", hash = "sha256:cad476d71ba72e8b941a8508d5b9ffb6b03e50f7102982474f085ad0d674b685"}, {file = "flake8_commas-4.0.0-py3-none-any.whl", hash = "sha256:cad476d71ba72e8b941a8508d5b9ffb6b03e50f7102982474f085ad0d674b685"},
{file = "flake8_commas-4.0.0.tar.gz", hash = "sha256:a68834b42a9a31c94ca790efe557a932c0eae21a3479c6b9a23c4dc077e3ea96"}, {file = "flake8_commas-4.0.0.tar.gz", hash = "sha256:a68834b42a9a31c94ca790efe557a932c0eae21a3479c6b9a23c4dc077e3ea96"},
@ -119,11 +128,12 @@ version = "1.0.0.dev0"
description = "HotPocket Workspace Tools" description = "HotPocket Workspace Tools"
optional = false optional = false
python-versions = "^3.12" python-versions = "^3.12"
groups = ["dev"]
files = [] files = []
develop = true develop = true
[package.dependencies] [package.dependencies]
invoke = "2.2.0" invoke = "2.2.1"
[package.source] [package.source]
type = "directory" type = "directory"
@ -131,13 +141,14 @@ url = "../packages/workspace_tools"
[[package]] [[package]]
name = "invoke" name = "invoke"
version = "2.2.0" version = "2.2.1"
description = "Pythonic task execution" description = "Pythonic task execution"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["dev"]
files = [ files = [
{file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}, {file = "invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8"},
{file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, {file = "invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707"},
] ]
[[package]] [[package]]
@ -146,6 +157,7 @@ version = "0.13.13"
description = "IPython-enabled pdb" description = "IPython-enabled pdb"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
groups = ["dev"]
files = [ files = [
{file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"},
{file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"},
@ -157,13 +169,14 @@ ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""}
[[package]] [[package]]
name = "ipython" name = "ipython"
version = "9.3.0" version = "9.6.0"
description = "IPython: Productive Interactive Computing" description = "IPython: Productive Interactive Computing"
optional = false optional = false
python-versions = ">=3.11" python-versions = ">=3.11"
groups = ["dev"]
files = [ files = [
{file = "ipython-9.3.0-py3-none-any.whl", hash = "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04"}, {file = "ipython-9.6.0-py3-none-any.whl", hash = "sha256:5f77efafc886d2f023442479b8149e7d86547ad0a979e9da9f045d252f648196"},
{file = "ipython-9.3.0.tar.gz", hash = "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8"}, {file = "ipython-9.6.0.tar.gz", hash = "sha256:5603d6d5d356378be5043e69441a072b50a5b33b4503428c77b04cb8ce7bc731"},
] ]
[package.dependencies] [package.dependencies]
@ -181,10 +194,10 @@ traitlets = ">=5.13.0"
[package.extras] [package.extras]
all = ["ipython[doc,matplotlib,test,test-extra]"] all = ["ipython[doc,matplotlib,test,test-extra]"]
black = ["black"] black = ["black"]
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"] doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[matplotlib,test]", "setuptools (>=61.2)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"]
matplotlib = ["matplotlib"] matplotlib = ["matplotlib (>3.7)"]
test = ["packaging", "pytest", "pytest-asyncio (<0.22)", "testpath"] test = ["packaging", "pytest", "pytest-asyncio", "testpath"]
test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbclient", "nbformat", "numpy (>=1.23)", "pandas", "trio"] test-extra = ["curio", "ipykernel", "ipython[matplotlib]", "ipython[test]", "jupyter_ai", "nbclient", "nbformat", "numpy (>=1.25)", "pandas (>2.0)", "trio"]
[[package]] [[package]]
name = "ipython-pygments-lexers" name = "ipython-pygments-lexers"
@ -192,6 +205,7 @@ version = "1.1.1"
description = "Defines a variety of Pygments lexers for highlighting IPython code." description = "Defines a variety of Pygments lexers for highlighting IPython code."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"}, {file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"},
{file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"}, {file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"},
@ -202,13 +216,14 @@ pygments = "*"
[[package]] [[package]]
name = "isort" name = "isort"
version = "6.0.1" version = "7.0.0"
description = "A Python utility / library to sort Python imports." description = "A Python utility / library to sort Python imports."
optional = false optional = false
python-versions = ">=3.9.0" python-versions = ">=3.10.0"
groups = ["dev"]
files = [ files = [
{file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, {file = "isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1"},
{file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, {file = "isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187"},
] ]
[package.extras] [package.extras]
@ -221,6 +236,7 @@ version = "0.19.2"
description = "An autocompletion tool for Python that can be used for text editors." description = "An autocompletion tool for Python that can be used for text editors."
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["dev"]
files = [ files = [
{file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
{file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
@ -240,6 +256,7 @@ version = "0.1.7"
description = "Inline Matplotlib backend for Jupyter" description = "Inline Matplotlib backend for Jupyter"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
@ -254,6 +271,7 @@ version = "0.7.0"
description = "McCabe checker, plugin for flake8" description = "McCabe checker, plugin for flake8"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["dev"]
files = [ files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
@ -261,43 +279,50 @@ files = [
[[package]] [[package]]
name = "mypy" name = "mypy"
version = "1.16.1" version = "1.18.2"
description = "Optional static typing for Python" description = "Optional static typing for Python"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"]
files = [ files = [
{file = "mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a"}, {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"},
{file = "mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72"}, {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"},
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea"}, {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"},
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574"}, {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"},
{file = "mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d"}, {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"},
{file = "mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6"}, {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"},
{file = "mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc"}, {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"},
{file = "mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782"}, {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"},
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507"}, {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"},
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca"}, {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"},
{file = "mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4"}, {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"},
{file = "mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6"}, {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"},
{file = "mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d"}, {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"},
{file = "mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9"}, {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"},
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79"}, {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"},
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15"}, {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"},
{file = "mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd"}, {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"},
{file = "mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b"}, {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"},
{file = "mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438"}, {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"},
{file = "mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536"}, {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"},
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f"}, {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"},
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359"}, {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"},
{file = "mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be"}, {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"},
{file = "mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee"}, {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"},
{file = "mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069"}, {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"},
{file = "mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da"}, {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"},
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c"}, {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"},
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383"}, {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"},
{file = "mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40"}, {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"},
{file = "mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b"}, {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"},
{file = "mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37"}, {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"},
{file = "mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab"}, {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"},
{file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"},
{file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"},
{file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"},
{file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"},
{file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"},
{file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"},
] ]
[package.dependencies] [package.dependencies]
@ -318,6 +343,7 @@ version = "1.1.0"
description = "Type system extensions for programs checked with the mypy type checker." description = "Type system extensions for programs checked with the mypy type checker."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
@ -329,6 +355,7 @@ version = "0.8.5"
description = "A Python Parser" description = "A Python Parser"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["dev"]
files = [ files = [
{file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"}, {file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"},
{file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"}, {file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"},
@ -344,6 +371,7 @@ version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths." description = "Utility library for gitignore style pattern matching of file paths."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@ -355,6 +383,8 @@ version = "4.9.0"
description = "Pexpect allows easy control of interactive console applications." description = "Pexpect allows easy control of interactive console applications."
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["dev"]
markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
files = [ files = [
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
@ -369,6 +399,7 @@ version = "3.0.52"
description = "Library for building powerful interactive command lines in Python" description = "Library for building powerful interactive command lines in Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"}, {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"},
{file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"}, {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"},
@ -383,6 +414,8 @@ version = "0.7.0"
description = "Run a subprocess in a pseudo terminal" description = "Run a subprocess in a pseudo terminal"
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["dev"]
markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
files = [ files = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
@ -394,6 +427,7 @@ version = "0.2.3"
description = "Safely evaluate AST nodes without side effects" description = "Safely evaluate AST nodes without side effects"
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["dev"]
files = [ files = [
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
@ -408,6 +442,7 @@ version = "2.14.0"
description = "Python style guide checker" description = "Python style guide checker"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"]
files = [ files = [
{file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"},
{file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"},
@ -419,6 +454,7 @@ version = "3.4.0"
description = "passive checker of Python programs" description = "passive checker of Python programs"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"]
files = [ files = [
{file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"},
{file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"},
@ -430,6 +466,7 @@ version = "2.19.2"
description = "Pygments is a syntax highlighting package written in Python." description = "Pygments is a syntax highlighting package written in Python."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
@ -444,6 +481,7 @@ version = "0.6.3"
description = "Extract data from python stack frames and tracebacks for informative displays" description = "Extract data from python stack frames and tracebacks for informative displays"
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["dev"]
files = [ files = [
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
@ -463,6 +501,7 @@ version = "5.14.3"
description = "Traitlets Python configuration system" description = "Traitlets Python configuration system"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"]
files = [ files = [
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
@ -478,6 +517,7 @@ version = "4.15.0"
description = "Backported and Experimental Type Hints for Python 3.9+" description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"]
files = [ files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
@ -489,6 +529,7 @@ version = "2025.2"
description = "Provider of IANA time zone data" description = "Provider of IANA time zone data"
optional = false optional = false
python-versions = ">=2" python-versions = ">=2"
groups = ["dev"]
files = [ files = [
{file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
@ -496,16 +537,17 @@ files = [
[[package]] [[package]]
name = "wcwidth" name = "wcwidth"
version = "0.2.13" version = "0.2.14"
description = "Measures the displayed width of unicode strings in a terminal" description = "Measures the displayed width of unicode strings in a terminal"
optional = false optional = false
python-versions = "*" python-versions = ">=3.6"
groups = ["dev"]
files = [ files = [
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"},
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"},
] ]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.1"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "1c74b6d5928a0b1bde41962f4233af2eba2242324c3155b21093b2f46baf5cd0" content-hash = "b03ef0369277d183d033049206a4cfd5f450473179995a4a79165fbb84d81fa0"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "hotpocket-apple" name = "hotpocket-apple"
version = "25.10.4" version = "25.10.21"
description = "HotPocket Apple Integrations" description = "HotPocket Apple Integrations"
authors = ["Tomek Wójcik <contact@bthlabs.pl>"] authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
license = "Apache-2.0" license = "Apache-2.0"
@ -15,11 +15,10 @@ factory-boy = "3.3.3"
flake8 = "7.3.0" flake8 = "7.3.0"
flake8-commas = "4.0.0" flake8-commas = "4.0.0"
hotpocket-workspace-tools = {path = "../packages/workspace_tools", develop = true} hotpocket-workspace-tools = {path = "../packages/workspace_tools", develop = true}
invoke = "2.2.0"
ipdb = "0.13.13" ipdb = "0.13.13"
ipython = "9.3.0" ipython = "9.6.0"
isort = "6.0.1" isort = "7.0.0"
mypy = "1.16.1" mypy = "1.18.2"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

View File

@ -2,22 +2,17 @@ ARG APP_USER_UID=1000
ARG APP_USER_GID=1000 ARG APP_USER_GID=1000
ARG IMAGE_ID=development.00000000 ARG IMAGE_ID=development.00000000
FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:build-node-20250819-01 AS development FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:build-node-20251014-01 AS development
ARG APP_USER_UID ARG APP_USER_UID
ARG APP_USER_GID ARG APP_USER_GID
ARG IMAGE_ID ARG IMAGE_ID
USER root
COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/bin/*.sh /srv/bin/ COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/bin/*.sh /srv/bin/
RUN chown -R ${APP_USER_UID}:${APP_USER_GID} /srv
USER app
VOLUME ["/srv/node_modules", "/srv/venv"] VOLUME ["/srv/node_modules", "/srv/venv"]
FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:build-python-20250819-01 AS deployment-build FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:build-python-20251014-01 AS deployment-build
ARG APP_USER_UID ARG APP_USER_UID
ARG APP_USER_GID ARG APP_USER_GID
@ -36,7 +31,7 @@ RUN poetry install --only main,deployment && \
rm -f hotpocket_backend/settings/deployment/build.py && \ rm -f hotpocket_backend/settings/deployment/build.py && \
rm -rf node_modules/ rm -rf node_modules/
FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:base-20250819-01 AS deployment-base FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:base-20251014-01 AS deployment-base
ARG APP_USER_UID ARG APP_USER_UID
ARG APP_USER_GID ARG APP_USER_GID
@ -50,7 +45,6 @@ COPY --from=deployment-build /srv/packages /srv/packages
COPY --from=deployment-build /srv/venv /srv/venv COPY --from=deployment-build /srv/venv /srv/venv
COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/bin/*.sh /srv/bin/ COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/bin/*.sh /srv/bin/
COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/deployment/gunicorn.conf.py backend/ops/deployment/gunicorn.logging.conf /srv/lib/ COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/deployment/gunicorn.conf.py backend/ops/deployment/gunicorn.logging.conf /srv/lib/
RUN chown -R $APP_USER_UID:$APP_USER_GID /srv
USER root USER root
@ -109,5 +103,4 @@ COPY --chown=$APP_USER_UID:$APP_USER_GID packages/ /srv/packages/
COPY --chown=$APP_USER_UID:$APP_USER_GID tls/ /srv/tls/ COPY --chown=$APP_USER_UID:$APP_USER_GID tls/ /srv/tls/
RUN ln -s /srv/app/ops/docker/settings /srv/app/hotpocket_backend/settings/docker && \ RUN ln -s /srv/app/ops/docker/settings /srv/app/hotpocket_backend/settings/docker && \
ln -s /srv/app/ops/docker/secrets /srv/app/hotpocket_backend/secrets/docker && \ ln -s /srv/app/ops/docker/secrets /srv/app/hotpocket_backend/secrets/docker
chown -R $APP_USER_UID:$APP_USER_GID /srv

View File

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

View File

@ -68,4 +68,10 @@ class Account(AbstractUser):
else: else:
result['auto_load_embeds'] = auto_load_embeds result['auto_load_embeds'] = auto_load_embeds
light_mode = result.get('light_mode', None)
if isinstance(light_mode, str) is True:
result['light_mode'] = (light_mode == 'True')
else:
result['light_mode'] = light_mode
return result return result

View File

@ -6,6 +6,7 @@ import hmac
import logging import logging
import uuid import uuid
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
import uuid6 import uuid6
@ -15,6 +16,10 @@ from hotpocket_soa.dto.accounts import (
AccessTokenMetaUpdateIn, AccessTokenMetaUpdateIn,
AccessTokensQuery, AccessTokensQuery,
) )
from hotpocket_soa.exceptions.backend import (
Invalid as InvalidError,
NotFound as NotFoundError,
)
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -23,7 +28,10 @@ class AccessTokensService:
class AccessTokensServiceError(Exception): class AccessTokensServiceError(Exception):
pass pass
class AccessTokenNotFound(AccessTokensServiceError): class Invalid(InvalidError, AccessTokensServiceError):
pass
class NotFound(NotFoundError, AccessTokensServiceError):
pass pass
def create(self, def create(self,
@ -32,6 +40,7 @@ class AccessTokensService:
origin: str, origin: str,
meta: dict, meta: dict,
) -> AccessToken: ) -> AccessToken:
try:
pk = uuid6.uuid7() pk = uuid6.uuid7()
key = hmac.new( key = hmac.new(
settings.SECRET_KEY.encode('ascii'), settings.SECRET_KEY.encode('ascii'),
@ -46,6 +55,8 @@ class AccessTokensService:
origin=origin, origin=origin,
meta=meta, meta=meta,
) )
except ValidationError as exception:
raise self.Invalid.from_django_validation_error(exception)
def get(self, *, pk: uuid.UUID) -> AccessToken: def get(self, *, pk: uuid.UUID) -> AccessToken:
try: try:
@ -53,7 +64,7 @@ class AccessTokensService:
return query_set.get(pk=pk) return query_set.get(pk=pk)
except AccessToken.DoesNotExist as exception: except AccessToken.DoesNotExist as exception:
raise self.AccessTokenNotFound( raise self.NotFound(
f'Access Token not found: pk=`{pk}`', f'Access Token not found: pk=`{pk}`',
) from exception ) from exception
@ -63,7 +74,7 @@ class AccessTokensService:
return query_set.get(key=key) return query_set.get(key=key)
except AccessToken.DoesNotExist as exception: except AccessToken.DoesNotExist as exception:
raise self.AccessTokenNotFound( raise self.NotFound(
f'Access Token not found: key=`{key}`', f'Access Token not found: key=`{key}`',
) from exception ) from exception
@ -98,7 +109,7 @@ class AccessTokensService:
pk: uuid.UUID, pk: uuid.UUID,
update: AccessTokenMetaUpdateIn, update: AccessTokenMetaUpdateIn,
) -> AccessToken: ) -> AccessToken:
access_token = AccessToken.active_objects.get(pk=pk) access_token = self.get(pk=pk)
next_meta = { next_meta = {
**(access_token.meta or {}), **(access_token.meta or {}),

View File

@ -5,6 +5,7 @@ import logging
import uuid import uuid
from hotpocket_backend.apps.accounts.models import Account from hotpocket_backend.apps.accounts.models import Account
from hotpocket_soa.exceptions.backend import NotFound as NotFoundError
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -13,7 +14,7 @@ class AccountsService:
class AccountsServiceError(Exception): class AccountsServiceError(Exception):
pass pass
class AccountNotFound(AccountsServiceError): class NotFound(NotFoundError, AccountsServiceError):
pass pass
def get(self, *, pk: uuid.UUID) -> Account: def get(self, *, pk: uuid.UUID) -> Account:
@ -22,6 +23,6 @@ class AccountsService:
return query_set.get(pk=pk) return query_set.get(pk=pk)
except Account.DoesNotExist as exception: except Account.DoesNotExist as exception:
raise self.AccountNotFound( raise self.NotFound(
f'Account not found: pk=`{pk}`', f'Account not found: pk=`{pk}`',
) from exception ) from exception

View File

@ -5,11 +5,17 @@ import datetime
import logging import logging
import uuid import uuid
from django.core.exceptions import ValidationError
from django.utils.timezone import now from django.utils.timezone import now
import uuid6 import uuid6
from hotpocket_backend.apps.accounts.models import AuthKey from hotpocket_backend.apps.accounts.models import AuthKey
from hotpocket_backend.apps.core.conf import settings from hotpocket_backend.apps.core.conf import settings
from hotpocket_soa.exceptions.backend import (
InternalError,
Invalid as InvalidError,
NotFound as NotFoundError,
)
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -18,22 +24,25 @@ class AuthKeysService:
class AuthKeysServiceError(Exception): class AuthKeysServiceError(Exception):
pass pass
class AuthKeyNotFound(AuthKeysServiceError): class Invalid(InvalidError, AuthKeysServiceError):
pass pass
class AuthKeyExpired(AuthKeysServiceError): class NotFound(NotFoundError, AuthKeysServiceError):
pass pass
class AuthKeyAccessDenied(AuthKeysServiceError): class Expired(InternalError, AuthKeysServiceError):
pass pass
def create(self, *, account_uuid: uuid.UUID) -> AuthKey: def create(self, *, account_uuid: uuid.UUID) -> AuthKey:
try:
key = str(uuid6.uuid7()) key = str(uuid6.uuid7())
return AuthKey.objects.create( return AuthKey.objects.create(
account_uuid=account_uuid, account_uuid=account_uuid,
key=key, key=key,
) )
except ValidationError as exception:
raise self.Invalid.from_django_validation_error(exception)
def get(self, *, pk: uuid.UUID) -> AuthKey: def get(self, *, pk: uuid.UUID) -> AuthKey:
try: try:
@ -41,7 +50,7 @@ class AuthKeysService:
return query_set.get(pk=pk) return query_set.get(pk=pk)
except AuthKey.DoesNotExist as exception: except AuthKey.DoesNotExist as exception:
raise self.AuthKeyNotFound( raise self.NotFound(
f'Auth Key not found: pk=`{pk}`', f'Auth Key not found: pk=`{pk}`',
) from exception ) from exception
@ -56,17 +65,17 @@ class AuthKeysService:
if ttl > 0: if ttl > 0:
if result.created_at < now() - datetime.timedelta(seconds=ttl): if result.created_at < now() - datetime.timedelta(seconds=ttl):
raise self.AuthKeyExpired( raise self.Expired(
f'Auth Key expired: pk=`{key}`', f'Auth Key expired: pk=`{key}`',
) )
if result.consumed_at is not None: if result.consumed_at is not None:
raise self.AuthKeyExpired( raise self.Expired(
f'Auth Key already consumed: pk=`{key}`', f'Auth Key already consumed: pk=`{key}`',
) )
return result return result
except AuthKey.DoesNotExist as exception: except AuthKey.DoesNotExist as exception:
raise self.AuthKeyNotFound( raise self.NotFound(
f'Auth Key not found: key=`{key}`', f'Auth Key not found: key=`{key}`',
) from exception ) from exception

View File

@ -1,16 +1,39 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import annotations from __future__ import annotations
import functools
import typing import typing
from bthlabs_jsonrpc_core.exceptions import BaseJSONRPCError
from bthlabs_jsonrpc_django import ( from bthlabs_jsonrpc_django import (
DjangoExecutor, DjangoExecutor,
DjangoJSONRPCSerializer, DjangoJSONRPCSerializer,
JSONRPCView as BaseJSONRPCView, JSONRPCView as BaseJSONRPCView,
) )
from django.core.exceptions import ValidationError
import uuid6 import uuid6
from hotpocket_soa.exceptions.frontend import SOAError
class SOAJSONRPCError(BaseJSONRPCError):
ERROR_CODE = -32000
ERROR_MESSAGE = 'SOA Error'
def to_rpc(self) -> dict:
exception = typing.cast(SOAError, self.data)
code = (
exception.code
if exception.code is not None
else self.ERROR_CODE
)
return {
'code': code,
'message': exception.message or self.ERROR_MESSAGE,
'data': exception.data,
}
class JSONRPCSerializer(DjangoJSONRPCSerializer): class JSONRPCSerializer(DjangoJSONRPCSerializer):
STRING_COERCIBLE_TYPES: typing.Any = ( STRING_COERCIBLE_TYPES: typing.Any = (
@ -18,30 +41,6 @@ class JSONRPCSerializer(DjangoJSONRPCSerializer):
uuid6.UUID, uuid6.UUID,
) )
def serialize_value(self, value: typing.Any) -> typing.Any:
if isinstance(value, ValidationError):
result: typing.Any = None
if hasattr(value, 'error_dict') is True:
result = {}
for field, errors in value.error_dict.items():
result[field] = [
error.code
for error
in errors
]
elif hasattr(value, 'error_list') is True:
result = [
error.code
for error in value.error_list
]
else:
result = value.code
return self.serialize_value(result)
return super().serialize_value(value)
class Executor(DjangoExecutor): class Executor(DjangoExecutor):
serializer = JSONRPCSerializer serializer = JSONRPCSerializer
@ -49,3 +48,14 @@ class Executor(DjangoExecutor):
class JSONRPCView(BaseJSONRPCView): class JSONRPCView(BaseJSONRPCView):
executor = Executor executor = Executor
def wrap_soa_errors(func: typing.Callable) -> typing.Callable:
@functools.wraps(func)
def decorator(*args, **kwargs):
try:
return func(*args, **kwargs)
except SOAError as exception:
raise SOAJSONRPCError(exception)
return decorator

View File

@ -5,6 +5,7 @@ import datetime
import logging import logging
import uuid import uuid
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
@ -15,6 +16,10 @@ from hotpocket_soa.dto.associations import (
AssociationsQuery, AssociationsQuery,
AssociationUpdateIn, AssociationUpdateIn,
) )
from hotpocket_soa.exceptions.backend import (
Invalid as InvalidError,
NotFound as NotFoundError,
)
from .saves import SavesService from .saves import SavesService
@ -25,7 +30,10 @@ class AssociationsService:
class AssociationsServiceError(Exception): class AssociationsServiceError(Exception):
pass pass
class AssociationNotFound(AssociationsServiceError): class Invalid(InvalidError, AssociationsServiceError):
pass
class NotFound(NotFoundError, AssociationsServiceError):
pass pass
@property @property
@ -46,6 +54,7 @@ class AssociationsService:
pk: uuid.UUID | None = None, pk: uuid.UUID | None = None,
created_at: datetime.datetime | None = None, created_at: datetime.datetime | None = None,
) -> Association: ) -> Association:
try:
save = SavesService().get(pk=save_uuid) save = SavesService().get(pk=save_uuid)
defaults = dict( defaults = dict(
@ -70,6 +79,8 @@ class AssociationsService:
result.save() result.save()
return result return result
except ValidationError as exception:
raise self.Invalid.from_django_validation_error(exception)
def get(self, def get(self,
*, *,
@ -87,7 +98,7 @@ class AssociationsService:
return query_set.get(pk=pk) return query_set.get(pk=pk)
except Association.DoesNotExist as exception: except Association.DoesNotExist as exception:
raise self.AssociationNotFound( raise self.NotFound(
f'Association not found: pk=`{pk}`', f'Association not found: pk=`{pk}`',
) from exception ) from exception
@ -112,6 +123,7 @@ class AssociationsService:
pk: uuid.UUID, pk: uuid.UUID,
update: AssociationUpdateIn, update: AssociationUpdateIn,
) -> Association: ) -> Association:
try:
association = self.get(pk=pk) association = self.get(pk=pk)
association.target_title = update.target_title association.target_title = update.target_title
association.target_description = update.target_description association.target_description = update.target_description
@ -127,6 +139,8 @@ class AssociationsService:
association.save() association.save()
return association return association
except ValidationError as exception:
raise self.Invalid.from_django_validation_error(exception)
def archive(self, *, pk: uuid.UUID) -> bool: def archive(self, *, pk: uuid.UUID) -> bool:
association = self.get(pk=pk) association = self.get(pk=pk)

View File

@ -5,19 +5,27 @@ import hashlib
import typing import typing
import uuid import uuid
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from hotpocket_backend.apps.core.services import get_adapter from hotpocket_backend.apps.core.services import get_adapter
from hotpocket_backend.apps.saves.models import Save from hotpocket_backend.apps.saves.models import Save
from hotpocket_backend.apps.saves.types import PSaveAdapter from hotpocket_backend.apps.saves.types import PSaveAdapter
from hotpocket_soa.dto.saves import ImportedSaveIn, SaveIn, SavesQuery from hotpocket_soa.dto.saves import ImportedSaveIn, SaveIn, SavesQuery
from hotpocket_soa.exceptions.backend import (
Invalid as InvalidError,
NotFound as NotFoundError,
)
class SavesService: class SavesService:
class SavesServiceError(Exception): class SavesServiceError(Exception):
pass pass
class SaveNotFound(SavesServiceError): class Invalid(InvalidError, SavesServiceError):
pass
class NotFound(NotFoundError, SavesServiceError):
pass pass
@property @property
@ -36,6 +44,7 @@ class SavesService:
account_uuid: uuid.UUID, account_uuid: uuid.UUID,
save: SaveIn | ImportedSaveIn, save: SaveIn | ImportedSaveIn,
) -> Save: ) -> Save:
try:
key = hashlib.sha256(save.url.encode('utf-8')).hexdigest() key = hashlib.sha256(save.url.encode('utf-8')).hexdigest()
defaults = dict( defaults = dict(
@ -59,12 +68,14 @@ class SavesService:
save_object.save() save_object.save()
return save_object return save_object
except ValidationError as exception:
raise self.Invalid.from_django_validation_error(exception)
def get(self, *, pk: uuid.UUID) -> Save: def get(self, *, pk: uuid.UUID) -> Save:
try: try:
return Save.active_objects.get(pk=pk) return Save.active_objects.get(pk=pk)
except Save.DoesNotExist as exception: except Save.DoesNotExist as exception:
raise self.SaveNotFound( raise self.NotFound(
f'Save not found: pk=`{pk}`', f'Save not found: pk=`{pk}`',
) from exception ) from exception

View File

@ -46,3 +46,39 @@ def version(request: HttpRequest) -> dict:
return { return {
'VERSION': backend_version, 'VERSION': backend_version,
} }
def appearance_settings(request: HttpRequest) -> dict:
theme = 'hotpocket'
result = {
'theme': theme,
'light_mode': False,
'theme_css': 'ui/css/bootstrap-hotpocket.min.css',
}
if request.user.is_anonymous is False:
theme = request.user.settings.get('theme', 'hotpocket')
result.update({
'theme': theme,
'light_mode': request.user.settings['light_mode'],
})
match theme:
case 'bootstrap':
result['theme_css'] = 'ui/css/bootstrap.min.css'
case 'cosmo':
result['theme_css'] = 'ui/css/cosmo.min.css'
case 'solar':
result['theme_css'] = 'ui/css/solar.min.css'
case 'sandstone':
result['theme_css'] = 'ui/css/sandstone.min.css'
case 'sketchy':
result['theme_css'] = 'ui/css/sketchy.min.css'
return {
'APPEARANCE_SETTINGS': result,
}

View File

@ -143,13 +143,20 @@ class SettingsForm(Form):
theme = forms.ChoiceField( theme = forms.ChoiceField(
label=_('Theme'), label=_('Theme'),
disabled=True, disabled=False,
required=False, required=False,
choices=[ choices=[
(None, _('Bootstrap')), (None, _('BTHLabs')),
('bootstrap', _('Bootstrap')),
('cosmo', _('Cosmo')), ('cosmo', _('Cosmo')),
('solar', _('Solar')),
('sandstone', _('Sandstone')),
('sketchy', _('Sketchy')),
], ],
show_hidden_initial=True, )
light_mode = forms.BooleanField(
label=_('Use light mode'),
required=False,
) )
auto_load_embeds = forms.ChoiceField( auto_load_embeds = forms.ChoiceField(
label=_('Auto load embedded content'), label=_('Auto load embedded content'),
@ -168,6 +175,7 @@ class SettingsForm(Form):
def get_layout_fields(self) -> list[str]: def get_layout_fields(self) -> list[str]:
return [ return [
'theme', 'theme',
'light_mode',
'auto_load_embeds', 'auto_load_embeds',
] ]

View File

@ -7,6 +7,7 @@ from bthlabs_jsonrpc_core import register_method
from django import db from django import db
from django.http import HttpRequest from django.http import HttpRequest
from hotpocket_backend.apps.core.rpc import wrap_soa_errors
from hotpocket_soa.services import ( from hotpocket_soa.services import (
AccessTokensService, AccessTokensService,
AccountsService, AccountsService,
@ -17,6 +18,7 @@ LOGGER = logging.getLogger(__name__)
@register_method('accounts.access_tokens.create', namespace='accounts') @register_method('accounts.access_tokens.create', namespace='accounts')
@wrap_soa_errors
def create(request: HttpRequest, def create(request: HttpRequest,
auth_key: str, auth_key: str,
meta: dict, meta: dict,
@ -27,7 +29,7 @@ def create(request: HttpRequest,
account_uuid=None, account_uuid=None,
key=auth_key, key=auth_key,
) )
except AuthKeysService.AuthKeyNotFound as exception: except AuthKeysService.NotFound as exception:
LOGGER.error( LOGGER.error(
'Unable to issue access token: %s', 'Unable to issue access token: %s',
exception, exception,
@ -37,7 +39,7 @@ def create(request: HttpRequest,
try: try:
account = AccountsService().get(pk=auth_key_object.account_uuid) account = AccountsService().get(pk=auth_key_object.account_uuid)
except AccountsService.AccountNotFound as exception: except AccountsService.NotFound as exception:
LOGGER.error( LOGGER.error(
'Unable to issue access token: %s', 'Unable to issue access token: %s',
exception, exception,

View File

@ -44,7 +44,7 @@ def check_access_token(request: HttpRequest,
access_token=access_token_object, access_token=access_token_object,
update=meta_update, update=meta_update,
) )
except AccessTokensService.AccessTokenNotFound as exception: except AccessTokensService.NotFound as exception:
LOGGER.error( LOGGER.error(
'Access Token not found: account_uuid=`%s` key=`%s`', 'Access Token not found: account_uuid=`%s` key=`%s`',
request.user.pk, request.user.pk,
@ -52,7 +52,7 @@ def check_access_token(request: HttpRequest,
exc_info=exception, exc_info=exception,
) )
result = False result = False
except AccessTokensService.AccessTokenAccessDenied as exception: except AccessTokensService.AccessDenied as exception:
LOGGER.error( LOGGER.error(
'Access Token access denied: account_uuid=`%s` key=`%s`', 'Access Token access denied: account_uuid=`%s` key=`%s`',
request.user.pk, request.user.pk,

View File

@ -4,11 +4,13 @@ from __future__ import annotations
from bthlabs_jsonrpc_core import register_method from bthlabs_jsonrpc_core import register_method
from django.http import HttpRequest from django.http import HttpRequest
from hotpocket_backend.apps.core.rpc import wrap_soa_errors
from hotpocket_backend.apps.ui.services.workflows import CreateSaveWorkflow from hotpocket_backend.apps.ui.services.workflows import CreateSaveWorkflow
from hotpocket_soa.dto.associations import AssociationOut from hotpocket_soa.dto.associations import AssociationOut
@register_method(method='saves.create') @register_method(method='saves.create')
@wrap_soa_errors
def create(request: HttpRequest, url: str) -> AssociationOut: def create(request: HttpRequest, url: str) -> AssociationOut:
association = CreateSaveWorkflow().run_rpc( association = CreateSaveWorkflow().run_rpc(
request=request, request=request,

View File

@ -27,7 +27,7 @@ class UIAccessTokensService:
account_uuid=account_uuid, account_uuid=account_uuid,
pk=pk, pk=pk,
) )
except AccessTokensService.AccessTokenNotFound as exception: except AccessTokensService.NotFound as exception:
LOGGER.error( LOGGER.error(
'Access Token not found: account_uuid=`%s` pk=`%s`', 'Access Token not found: account_uuid=`%s` pk=`%s`',
account_uuid, account_uuid,
@ -35,7 +35,7 @@ class UIAccessTokensService:
exc_info=exception, exc_info=exception,
) )
raise Http404() raise Http404()
except AccessTokensService.AccessTokenAccessDenied as exception: except AccessTokensService.AccessDenied as exception:
LOGGER.error( LOGGER.error(
'Access Token access denied: account_uuid=`%s` pk=`%s`', 'Access Token access denied: account_uuid=`%s` pk=`%s`',
account_uuid, account_uuid,

View File

@ -34,7 +34,7 @@ class UIAssociationsService:
with_target=True, with_target=True,
allow_archived=allow_archived, allow_archived=allow_archived,
) )
except AssociationsService.AssociationNotFound as exception: except AssociationsService.NotFound as exception:
LOGGER.error( LOGGER.error(
'Association not found: account_uuid=`%s` pk=`%s`', 'Association not found: account_uuid=`%s` pk=`%s`',
account_uuid, account_uuid,
@ -42,7 +42,7 @@ class UIAssociationsService:
exc_info=exception, exc_info=exception,
) )
raise Http404() raise Http404()
except AssociationsService.AssociationAccessDenied as exception: except AssociationsService.AccessDenied as exception:
LOGGER.error( LOGGER.error(
'Association access denied: account_uuid=`%s` pk=`%s`', 'Association access denied: account_uuid=`%s` pk=`%s`',
account_uuid, account_uuid,

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import csv import csv
import datetime import datetime
import logging
import os import os
import uuid import uuid
@ -13,16 +14,28 @@ from django.utils.timezone import get_current_timezone
from hotpocket_backend.apps.ui.services.workflows import ImportSaveWorkflow from hotpocket_backend.apps.ui.services.workflows import ImportSaveWorkflow
from hotpocket_backend.apps.ui.tasks import import_from_pocket from hotpocket_backend.apps.ui.tasks import import_from_pocket
from hotpocket_common.uuid import uuid7_from_timestamp from hotpocket_common.uuid import uuid7_from_timestamp
from hotpocket_soa.services import SavesService
LOGGER = logging.getLogger(__name__)
class UIImportsService: class UIImportsService:
def import_from_pocket(self, def import_from_pocket(self,
*, *,
job: str,
account_uuid: uuid.UUID, account_uuid: uuid.UUID,
csv_path: str, csv_path: str,
) -> list[tuple[uuid.UUID, uuid.UUID]]: ) -> list[tuple[uuid.UUID, uuid.UUID]]:
result = [] LOGGER.info(
'Starting import job: job=`%s` account_uuid=`%s`',
job,
account_uuid,
extra={
'job': job,
},
)
result = []
with db.transaction.atomic(): with db.transaction.atomic():
try: try:
with open(csv_path, 'r', encoding='utf-8') as csv_file: with open(csv_path, 'r', encoding='utf-8') as csv_file:
@ -34,13 +47,14 @@ class UIImportsService:
current_timezone = get_current_timezone() current_timezone = get_current_timezone()
is_header = False is_header = False
for row in csv_reader: for row_number, row in enumerate(csv_reader, start=1):
if is_header is False: if is_header is False:
is_header = True is_header = True
continue continue
timestamp = int(row['time_added']) timestamp = int(row['time_added'])
try:
save, association = ImportSaveWorkflow().run( save, association = ImportSaveWorkflow().run(
account_uuid=account_uuid, account_uuid=account_uuid,
url=row['url'], url=row['url'],
@ -50,6 +64,18 @@ class UIImportsService:
timestamp, tz=current_timezone, timestamp, tz=current_timezone,
), ),
) )
except SavesService.Invalid as exception:
LOGGER.error(
'Import error: row_number=`%d` url=`%s` exception=`%s`',
row_number,
row['url'],
exception,
exc_info=exception,
extra={
'job': job,
},
)
continue
result.append((save.pk, association.pk)) result.append((save.pk, association.pk))
finally: finally:
@ -64,6 +90,7 @@ class UIImportsService:
) -> AsyncResult: ) -> AsyncResult:
return import_from_pocket.apply_async( return import_from_pocket.apply_async(
kwargs={ kwargs={
'job': str(uuid.uuid4()),
'account_uuid': account_uuid, 'account_uuid': account_uuid,
'csv_path': csv_path, 'csv_path': csv_path,
}, },

View File

@ -19,7 +19,7 @@ class UISavesService:
def get_or_404(self, *, pk: uuid.UUID) -> SaveOut: def get_or_404(self, *, pk: uuid.UUID) -> SaveOut:
try: try:
return SavesService().get(pk=pk) return SavesService().get(pk=pk)
except SavesService.SaveNotFound as exception: except SavesService.NotFound as exception:
LOGGER.error( LOGGER.error(
'Save not found: pk=`%s`', pk, exc_info=exception, 'Save not found: pk=`%s`', pk, exc_info=exception,
) )

View File

@ -1,9 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import annotations from __future__ import annotations
from bthlabs_jsonrpc_core import JSONRPCInternalError
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import ValidationError
import django.db import django.db
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
@ -14,7 +12,6 @@ from hotpocket_backend.apps.accounts.types import PAccount
from hotpocket_soa.dto.associations import AssociationOut from hotpocket_soa.dto.associations import AssociationOut
from hotpocket_soa.dto.celery import AsyncResultOut from hotpocket_soa.dto.celery import AsyncResultOut
from hotpocket_soa.dto.saves import SaveIn, SaveOut from hotpocket_soa.dto.saves import SaveIn, SaveOut
from hotpocket_soa.services import SavesService
from .base import SaveWorkflow from .base import SaveWorkflow
@ -73,14 +70,8 @@ class CreateSaveWorkflow(SaveWorkflow):
account: PAccount, account: PAccount,
url: str, url: str,
) -> AssociationOut: ) -> AssociationOut:
try:
save, association, processing_result = self.create_associate_and_process( save, association, processing_result = self.create_associate_and_process(
account, url, account, url,
) )
return association return association
except SavesService.SavesServiceError as exception:
if isinstance(exception.__cause__, ValidationError) is True:
raise JSONRPCInternalError(data=exception.__cause__)
raise

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -11,6 +11,7 @@ LOGGER = logging.getLogger(__name__)
@shared_task @shared_task
def import_from_pocket(*, def import_from_pocket(*,
job: str,
account_uuid: uuid.UUID, account_uuid: uuid.UUID,
csv_path: str, csv_path: str,
) -> list[tuple[uuid.UUID, uuid.UUID]]: ) -> list[tuple[uuid.UUID, uuid.UUID]]:
@ -18,6 +19,7 @@ def import_from_pocket(*,
try: try:
return UIImportsService().import_from_pocket( return UIImportsService().import_from_pocket(
job=job,
account_uuid=account_uuid, account_uuid=account_uuid,
csv_path=csv_path, csv_path=csv_path,
) )

View File

@ -10,14 +10,18 @@
|_| |_|
production production
--> -->
<html lang="en" data-bs-theme="dark"> <html lang="en" data-bs-theme="{% if APPEARANCE_SETTINGS.light_mode %}light{% else %}dark{% endif %}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,user-scalable=no"> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,user-scalable=no">
<meta name="generator" content="pl.bthlabs.HotPocket.backend@{{ IMAGE_ID }}"> <meta name="generator" content="pl.bthlabs.HotPocket.backend@{{ IMAGE_ID }}">
{% if APPEARANCE_SETTINGS.light_mode %}
<meta name="theme-color" content="#f8f9fa" />
{% else %}
<meta name="theme-color" content="#2b3035" /> <meta name="theme-color" content="#2b3035" />
{% endif %}
<title>{% block title %}{% translate 'Not Found' %}{% endblock %} | {{ SITE_TITLE }}</title> <title>{% block title %}{% translate 'Not Found' %}{% endblock %} | {{ SITE_TITLE }}</title>
<link href="{% static 'ui/css/bootstrap.min.css' %}" rel="stylesheet"> <link href="{% static APPEARANCE_SETTINGS.theme_css %}" rel="stylesheet">
<link href="{% static 'ui/css/bootstrap-icons.min.css' %}" rel="stylesheet"> <link href="{% static 'ui/css/bootstrap-icons.min.css' %}" rel="stylesheet">
<link href="{% static 'ui/css/hotpocket-backend.css' %}" rel="stylesheet"> <link href="{% static 'ui/css/hotpocket-backend.css' %}" rel="stylesheet">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">

View File

@ -9,6 +9,10 @@ from hotpocket_backend.apps.core.conf import settings
def manifest_json(request: HttpRequest) -> JsonResponse: def manifest_json(request: HttpRequest) -> JsonResponse:
light_theme = False
if request.user.is_anonymous is False:
light_theme = request.user.settings.get('light_theme', None) or False
result = { result = {
'name': settings.SITE_TITLE, 'name': settings.SITE_TITLE,
'short_name': settings.SITE_SHORT_TITLE, 'short_name': settings.SITE_SHORT_TITLE,
@ -16,8 +20,17 @@ def manifest_json(request: HttpRequest) -> JsonResponse:
reverse('ui.associations.browse'), reverse('ui.associations.browse'),
), ),
'display': 'standalone', 'display': 'standalone',
'background_color': '#212529', 'background_color': (
'theme_color': '#2b3035', '#212529'
if light_theme is False
else '#ffffff'
),
'theme_color': (
'#2b3035'
if light_theme is False
else
'#f8f9fa'
),
'icons': [ 'icons': [
{ {
'src': request.build_absolute_uri( 'src': request.build_absolute_uri(

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import os import os
from .base import * # noqa: F403 from .webapp import * # noqa: F403
DEBUG = ( DEBUG = (
os.environ.get('HOTPOCKET_BACKEND_DEBUG', 'false').lower() == 'true' os.environ.get('HOTPOCKET_BACKEND_DEBUG', 'false').lower() == 'true'
@ -12,23 +12,8 @@ DEBUG = (
ALLOWED_HOSTS = os.environ.get('HOTPOCKET_BACKEND_ALLOWED_HOSTS', '*').split(',') ALLOWED_HOSTS = os.environ.get('HOTPOCKET_BACKEND_ALLOWED_HOSTS', '*').split(',')
INSTALLED_APPS += [ # noqa: F405
'crispy_forms',
'crispy_bootstrap5',
'django_htmx',
]
MIDDLEWARE = [ MIDDLEWARE = [
'hotpocket_backend.apps.core.middleware.RequestIDMiddleware', *MIDDLEWARE, # noqa: F405
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'social_django.middleware.SocialAuthExceptionMiddleware',
'django_htmx.middleware.HtmxMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware',
] ]

View File

@ -67,6 +67,7 @@ TEMPLATES = [
'hotpocket_backend.apps.ui.context_processors.htmx', 'hotpocket_backend.apps.ui.context_processors.htmx',
'hotpocket_backend.apps.ui.context_processors.debug', 'hotpocket_backend.apps.ui.context_processors.debug',
'hotpocket_backend.apps.ui.context_processors.version', 'hotpocket_backend.apps.ui.context_processors.version',
'hotpocket_backend.apps.ui.context_processors.appearance_settings',
], ],
}, },
}, },

View File

@ -13,7 +13,7 @@ cat <<EOF
|_| |_|
production production
HotPocket v25.10.4 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/) HotPocket v25.10.21 [${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

View File

@ -1,6 +1,6 @@
{ {
"name": "hotpocket-backend", "name": "hotpocket-backend",
"version": "25.10.4", "version": "25.10.21",
"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",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "hotpocket-backend" name = "hotpocket-backend"
version = "25.10.4" version = "25.10.21"
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"
@ -16,45 +16,45 @@ python = "^3.12"
bthlabs-jsonrpc-django = "1.2.0" bthlabs-jsonrpc-django = "1.2.0"
celery = "5.5.3" celery = "5.5.3"
crispy-bootstrap5 = "2025.6" crispy-bootstrap5 = "2025.6"
django = "5.2.3" django = "5.2.7"
django-cors-headers = "4.7.0" django-cors-headers = "4.9.0"
django-crispy-forms = "2.4" django-crispy-forms = "2.4"
django-htmx = "1.23.2" django-htmx = "1.26.0"
hotpocket-common = {path = "../packages/common", develop = true} hotpocket-common = {path = "../packages/common", develop = true}
hotpocket-soa = {path = "../packages/soa", develop = true} hotpocket-soa = {path = "../packages/soa", develop = true}
keep-it-secret = {version = "1.2.0", extras = ["aws", "vault"]} keep-it-secret = {version = "1.2.0", extras = ["aws", "vault"]}
psycopg = {version = "3.2.9", extras = ["binary"]} psycopg = {version = "3.2.10", extras = ["binary"]}
pydantic = "2.11.7" pydantic = "2.12.2"
pyquery = "2.0.1" pyquery = "2.0.1"
requests = "2.32.4" requests = "2.32.5"
social-auth-app-django = "5.5.1" social-auth-app-django = "5.6.0"
social-auth-core = "4.7.0" social-auth-core = "4.8.1"
sqlalchemy = "2.0.41" sqlalchemy = "2.0.44"
uuid6 = "2025.0.0" uuid6 = "2025.0.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
django-extensions = "4.1" django-extensions = "4.1"
factory-boy = "3.3.3" factory-boy = "3.3.3"
flake8 = "7.3.0" flake8 = "7.3.0"
flake8-commas = "4.0.0" flake8-commas = "4.0.0"
freezegun = "1.5.2" freezegun = "1.5.5"
hotpocket-backend-testing = {path = "testing", develop = true} hotpocket-backend-testing = {path = "testing", develop = true}
hotpocket-testing = {path = "../packages/testing", develop = true} hotpocket-testing = {path = "../packages/testing", develop = true}
hotpocket-workspace-tools = {path = "../packages/workspace_tools", develop = true} hotpocket-workspace-tools = {path = "../packages/workspace_tools", develop = true}
invoke = "2.2.0" invoke = "2.2.1"
ipdb = "0.13.13" ipdb = "0.13.13"
ipython = "9.3.0" ipython = "9.6.0"
isort = "6.0.1" isort = "7.0.0"
mypy = "1.16.1" mypy = "1.18.2"
pytest = "8.4.1" pytest = "8.4.2"
pytest-django = "4.11.1" pytest-django = "4.11.1"
pytest-env = "1.1.5" pytest-env = "1.2.0"
pytest-mock = "3.14.1" pytest-mock = "3.15.1"
[tool.poetry.group.deployment.dependencies] [tool.poetry.group.deployment.dependencies]
gunicorn = {version = "23.0.0", extras = ["setproctitle"]} gunicorn = {version = "23.0.0", extras = ["setproctitle"]}
gunicorn_worker_healthcheck = "1.0.0" gunicorn_worker_healthcheck = "1.0.0"
whitenoise = "6.9.0" whitenoise = "6.11.0"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

Some files were not shown because too many files have changed in this diff Show More