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 (