From 72155d4560bd461288440a41fe209d418274b91c Mon Sep 17 00:00:00 2001 From: Vontor Bruno Date: Thu, 18 Dec 2025 16:23:35 +0100 Subject: [PATCH] Refactor Zasilkovna client, update imports and cleanup Refactored the Zasilkovna SOAP client to use a singleton pattern with caching and lazy loading, improving reliability and startup performance. Updated invoice PDF generation to import WeasyPrint lazily, preventing startup failures on systems missing dependencies. Cleaned up unused imports and code in several frontend components, removed unused state and variables, and adjusted Docker frontend port mapping. Also updated Django migration files to reflect a new generation timestamp. --- backend/account/migrations/0001_initial.py | 2 +- backend/commerce/migrations/0001_initial.py | 2 +- backend/commerce/models.py | 12 ++++- backend/commerce/tasks.py | 4 ++ .../configuration/migrations/0001_initial.py | 2 +- .../downloader/migrations/0001_initial.py | 2 +- .../gopay/migrations/0001_initial.py | 2 +- .../stripe/migrations/0001_initial.py | 2 +- backend/thirdparty/zasilkovna/client.py | 49 +++++++++++++------ .../zasilkovna/migrations/0001_initial.py | 2 +- docker-compose.yml | 4 +- frontend/src/components/hero/HeroCarousel.tsx | 6 +++ frontend/src/components/navbar/SiteNav.tsx | 1 - frontend/src/context/AuthContext.tsx | 3 +- frontend/src/layouts/Default.tsx | 4 +- frontend/src/pages/downloader/Downloader.tsx | 2 - .../home/components/Services/Services.tsx | 5 -- .../home/components/Services/TradingGraph.tsx | 1 - .../home/components/Services/droneServis.tsx | 3 -- .../components/Services/kinematografie.tsx | 2 - .../pages/home/components/Services/webs.tsx | 3 -- 21 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 frontend/src/components/hero/HeroCarousel.tsx diff --git a/backend/account/migrations/0001_initial.py b/backend/account/migrations/0001_initial.py index c95b1fb..9e0eabf 100644 --- a/backend/account/migrations/0001_initial.py +++ b/backend/account/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 import account.models import django.contrib.auth.validators diff --git a/backend/commerce/migrations/0001_initial.py b/backend/commerce/migrations/0001_initial.py index d435ad9..b1e0cf1 100644 --- a/backend/commerce/migrations/0001_initial.py +++ b/backend/commerce/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 import django.core.validators import django.db.models.deletion diff --git a/backend/commerce/models.py b/backend/commerce/models.py index 0f76fc2..cd06b59 100644 --- a/backend/commerce/models.py +++ b/backend/commerce/models.py @@ -7,9 +7,9 @@ from django.template.loader import render_to_string from django.core.files.base import ContentFile from django.core.validators import MaxValueValidator, MinValueValidator -from weasyprint import HTML import os + from configuration.models import SiteConfiguration from thirdparty.zasilkovna.models import ZasilkovnaPacket @@ -582,6 +582,16 @@ class Invoice(models.Model): order = Order.objects.get(invoice=self) # Render HTML html_string = render_to_string("invoice/invoice.html", {"invoice": self}) + # Import WeasyPrint lazily to avoid startup failures when system + # libraries (Pango/GObject) are not present on Windows. + try: + + from weasyprint import HTML + except Exception as e: + raise RuntimeError( + "WeasyPrint is not available. Install its system dependencies (Pango/GTK) or run the backend in Docker." + ) from e + pdf_bytes = HTML(string=html_string).write_pdf() # Save directly to FileField diff --git a/backend/commerce/tasks.py b/backend/commerce/tasks.py index a3778fd..2393a0b 100644 --- a/backend/commerce/tasks.py +++ b/backend/commerce/tasks.py @@ -5,6 +5,10 @@ from django.apps import apps from django.utils import timezone +# -- CLEANUP TASKS -- + +# Delete expired/cancelled orders (older than 24 hours) +@shared_task def delete_expired_orders(): Order = apps.get_model('commerce', 'Order') diff --git a/backend/configuration/migrations/0001_initial.py b/backend/configuration/migrations/0001_initial.py index aeae3ff..182adee 100644 --- a/backend/configuration/migrations/0001_initial.py +++ b/backend/configuration/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 from django.db import migrations, models diff --git a/backend/thirdparty/downloader/migrations/0001_initial.py b/backend/thirdparty/downloader/migrations/0001_initial.py index 38f4dda..3274053 100644 --- a/backend/thirdparty/downloader/migrations/0001_initial.py +++ b/backend/thirdparty/downloader/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 from django.db import migrations, models diff --git a/backend/thirdparty/gopay/migrations/0001_initial.py b/backend/thirdparty/gopay/migrations/0001_initial.py index 162316c..69dc43a 100644 --- a/backend/thirdparty/gopay/migrations/0001_initial.py +++ b/backend/thirdparty/gopay/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 import django.db.models.deletion import django.utils.timezone diff --git a/backend/thirdparty/stripe/migrations/0001_initial.py b/backend/thirdparty/stripe/migrations/0001_initial.py index abd2ba4..871444c 100644 --- a/backend/thirdparty/stripe/migrations/0001_initial.py +++ b/backend/thirdparty/stripe/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 from django.db import migrations, models diff --git a/backend/thirdparty/zasilkovna/client.py b/backend/thirdparty/zasilkovna/client.py index 0e24a9f..cb2076a 100644 --- a/backend/thirdparty/zasilkovna/client.py +++ b/backend/thirdparty/zasilkovna/client.py @@ -1,28 +1,49 @@ from zeep import Client from zeep.exceptions import Fault +from zeep.transports import Transport +from zeep.cache import SqliteCache +import tempfile import base64 import logging import os +from functools import lru_cache + logger = logging.getLogger(__name__) WSDL_URL = os.getenv("PACKETA_WSDL_URL", "https://www.zasilkovna.cz/api/soap.wsdl") PACKETA_API_PASSWORD = os.getenv("PACKETA_API_PASSWORD") -zeepZasClient = Client(wsdl=WSDL_URL) + + +# --- 1. Singleton pro klienta (aby se WSDL stáhlo jen jednou pro celý proces) --- +@lru_cache(maxsize=1) +def get_shared_client(): + """ + Tato funkce vrátí instanci klienta a drží ji v tempfile na jednu hodinu. + """ + if not PACKETA_API_PASSWORD: + raise ValueError("Packeta API password is not set.") + + + tempFpath = os.path.join(tempfile.gettempdir(), 'zeep_packeta_cache.db') + transport = Transport(cache=SqliteCache(path=tempFpath, timeout=3600)) + + return Client(wsdl=WSDL_URL, transport=transport) class PacketaAPI: - #TODO: zeptat se jestli nepřidat další checkovací parametry ohledně zásilkovny např: blokování podle configurace webu - # popřemýšlet, jestli api klíče nenastavit přes configurator webu - def __getattribute__(self): - if PACKETA_API_PASSWORD in [None, ""]: - raise Exception("Packeta API password is not set in environment variables.") + # --- 2. Property pro lazy loading --- + @property + def client(self): + """ + Při zavolání self.client se zavolá tento kód. + Vrátí již existujícího klienta z cache. + """ + return get_shared_client() - elif zeepZasClient is None: - raise Exception("Packeta SOAP client is not initialized.") # ---------- CREATE PACKET METHODS ---------- @@ -68,7 +89,7 @@ class PacketaAPI: try: # Použijeme createPacketClaimWithPassword, protože umožňuje ukládat email a telefon - result = zeepZasClient.service.createPacketClaimWithPassword(PACKETA_API_PASSWORD, attributes) + result = self.client.service.createPacketClaimWithPassword(PACKETA_API_PASSWORD, attributes) return result except Fault as e: @@ -83,7 +104,7 @@ class PacketaAPI: packet_id = 1234567890 """ try: - zeepZasClient.service.cancelPacket(PACKETA_API_PASSWORD, packet_id) + self.client.service.cancelPacket(PACKETA_API_PASSWORD, packet_id) return {"status": "ok", "message": f"Zásilka {packet_id} byla zrušena."} @@ -99,7 +120,7 @@ class PacketaAPI: Vrací datum do kdy je uložená zásilka. """ try: - request = zeepZasClient.service.packetGetStoredUntil(PACKETA_API_PASSWORD, packet_id) + request = self.client.service.packetGetStoredUntil(PACKETA_API_PASSWORD, packet_id) if request is None: raise Exception(f"Zásilka {packet_id} nebyla ještě doručena.") @@ -128,7 +149,7 @@ class PacketaAPI: bytes: PDF soubor (base64 dekódovaný) """ try: - pdf_base64 = zeepZasClient.service.packetLabelPdf( + pdf_base64 = self.client.service.packetLabelPdf( PACKETA_API_PASSWORD, packet_id, format, offset ) @@ -163,7 +184,7 @@ class PacketaAPI: packet_ids = ["1234567890", "1234567891", "1234567892"] """ try: - result = zeepZasClient.service.createShipment(PACKETA_API_PASSWORD, packet_ids) + result = self.client.service.createShipment(PACKETA_API_PASSWORD, packet_ids) return result @@ -176,7 +197,7 @@ class PacketaAPI: Získá seznam balíků ve shipmentu podle jeho shipmentId. """ try: - result = zeepZasClient.service.shipmentPackets(PACKETA_API_PASSWORD, shipment_id) + result = self.client.service.shipmentPackets(PACKETA_API_PASSWORD, shipment_id) return result except Fault as e: logger.error(f"Packeta shipmentPackets error: {e}") diff --git a/backend/thirdparty/zasilkovna/migrations/0001_initial.py b/backend/thirdparty/zasilkovna/migrations/0001_initial.py index 41a057a..556f9ac 100644 --- a/backend/thirdparty/zasilkovna/migrations/0001_initial.py +++ b/backend/thirdparty/zasilkovna/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.9 on 2025-12-14 02:23 +# Generated by Django 5.2.7 on 2025-12-18 15:11 import django.core.validators from django.db import migrations, models diff --git a/docker-compose.yml b/docker-compose.yml index 15e9ddb..99f4741 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -93,8 +93,8 @@ services: env_file: - ./frontend/.env ports: - - 80:80 - # - 9000:80 + # - 80:80 + - 9000:80 depends_on: - backend networks: diff --git a/frontend/src/components/hero/HeroCarousel.tsx b/frontend/src/components/hero/HeroCarousel.tsx new file mode 100644 index 0000000..6d8ea60 --- /dev/null +++ b/frontend/src/components/hero/HeroCarousel.tsx @@ -0,0 +1,6 @@ +export default function HeroCarousel() { + return ( + <> + + ); +} \ No newline at end of file diff --git a/frontend/src/components/navbar/SiteNav.tsx b/frontend/src/components/navbar/SiteNav.tsx index ba01e9a..7f3f063 100644 --- a/frontend/src/components/navbar/SiteNav.tsx +++ b/frontend/src/components/navbar/SiteNav.tsx @@ -12,7 +12,6 @@ import { FaPlayCircle, FaUsers, FaHandsHelping, - FaProjectDiagram, } from "react-icons/fa"; import {FaClapperboard, FaCubes} from "react-icons/fa6"; import styles from "./navbar.module.css"; diff --git a/frontend/src/context/AuthContext.tsx b/frontend/src/context/AuthContext.tsx index fb451f0..4bb0f0d 100644 --- a/frontend/src/context/AuthContext.tsx +++ b/frontend/src/context/AuthContext.tsx @@ -1,4 +1,5 @@ -import { createContext, useContext, useState, useEffect, ReactNode } from "react"; +// import type { ReactNode } from "react"; +// import { createContext, useContext, useState, useEffect } from "react"; //TODO: připraveno pro použití jenom linknout funkce z vygenerovaného api klientan a logout() a currentUser() diff --git a/frontend/src/layouts/Default.tsx b/frontend/src/layouts/Default.tsx index 0995d79..c2d7f01 100644 --- a/frontend/src/layouts/Default.tsx +++ b/frontend/src/layouts/Default.tsx @@ -1,17 +1,17 @@ import Footer from "../components/Footer/footer"; import ContactMeForm from "../components/Forms/ContactMe/ContactMeForm"; -import HomeNav from "../components/navbar/HomeNav"; import Drone from "../components/ads/Drone/Drone"; import Portfolio from "../components/ads/Portfolio/Portfolio"; import Home from "../pages/home/home"; import { Outlet } from "react-router"; +import Navbar from "../components/navbar/SiteNav"; export default function HomeLayout(){ return( <> {/* Example usage of imported components, adjust as needed */} - + {}} onLogout={() => {}} /> {/*page*/}
diff --git a/frontend/src/pages/downloader/Downloader.tsx b/frontend/src/pages/downloader/Downloader.tsx index fc42164..e1e0820 100644 --- a/frontend/src/pages/downloader/Downloader.tsx +++ b/frontend/src/pages/downloader/Downloader.tsx @@ -1,5 +1,3 @@ -import { useEffect, useMemo, useState } from "react"; - export default function Downloader() { return ( diff --git a/frontend/src/pages/home/components/Services/Services.tsx b/frontend/src/pages/home/components/Services/Services.tsx index 6c45a63..4873d10 100644 --- a/frontend/src/pages/home/components/Services/Services.tsx +++ b/frontend/src/pages/home/components/Services/Services.tsx @@ -1,13 +1,8 @@ -import { useState } from "react"; - import KinematografieSection from "./kinematografie"; import DroneServisSection from "./droneServis"; import WebsiteServiceSection from "./webs"; -import styles from "./Services.module.css"; - export default function Services() { - const [active, setActive] = useState(null); return (
diff --git a/frontend/src/pages/home/components/Services/TradingGraph.tsx b/frontend/src/pages/home/components/Services/TradingGraph.tsx index c00a8b2..2ee03de 100644 --- a/frontend/src/pages/home/components/Services/TradingGraph.tsx +++ b/frontend/src/pages/home/components/Services/TradingGraph.tsx @@ -37,7 +37,6 @@ export default function TradingGraph() { const colorOther = styles.getPropertyValue("--c-other").trim(); const colorLines = styles.getPropertyValue("--c-lines").trim(); const colorBoxes = styles.getPropertyValue("--c-boxes").trim(); - const colorBg = styles.getPropertyValue("--c-background").trim(); // Set canvas size const rect = canvas.getBoundingClientRect(); diff --git a/frontend/src/pages/home/components/Services/droneServis.tsx b/frontend/src/pages/home/components/Services/droneServis.tsx index f83cba3..8bd9960 100644 --- a/frontend/src/pages/home/components/Services/droneServis.tsx +++ b/frontend/src/pages/home/components/Services/droneServis.tsx @@ -1,6 +1,3 @@ -import styles from "./Services.module.css"; - - export default function DroneServisSection() { return (
diff --git a/frontend/src/pages/home/components/Services/kinematografie.tsx b/frontend/src/pages/home/components/Services/kinematografie.tsx index 5970fad..2ad41fe 100644 --- a/frontend/src/pages/home/components/Services/kinematografie.tsx +++ b/frontend/src/pages/home/components/Services/kinematografie.tsx @@ -1,5 +1,3 @@ -import styles from "./Services.module.css"; - export default function KinematografieSection() { return (
diff --git a/frontend/src/pages/home/components/Services/webs.tsx b/frontend/src/pages/home/components/Services/webs.tsx index 7d6ffc2..1ea9a87 100644 --- a/frontend/src/pages/home/components/Services/webs.tsx +++ b/frontend/src/pages/home/components/Services/webs.tsx @@ -1,6 +1,3 @@ -import styles from "./Services.module.css"; -import TradingGraph from "./TradingGraph"; - export default function WebsiteServiceSection() { return (