🔴 Live demo → https://wms.damian-z.uk/
System zarządzania magazynem obejmujący pełny cykl przyjęcie → składowanie → wydanie: dane podstawowe produktów i lokalizacji, śledzenie partii i dat ważności, wielowymiarową pojemność lokalizacji, planowane odkładanie i kompletację, niezmienialny rejestr ruchów magazynowych oraz pulpit analityczny.
Backend to Web API na ASP.NET Core (.NET 10), zbudowane w czystej, warstwowej architekturze. Frontend to aplikacja jednostronicowa (SPA) we Vue 3. Dane trzymane są w PostgreSQL.
Storage / Quarantine / Returns), strefa temperaturowa (Ambient / Chilled / Frozen), pojemność w trzech wymiarach (jednostki / masa / objętość, każdy ograniczany niezależnie, a decyduje najbardziej restrykcyjny), reguły mieszania SKU i partii, stany aktywny i zablokowany oraz preferowane produkty dla danej lokalizacji.Draft → Putaway → Completed, z możliwością anulowania ze stanu Draft lub Putaway.PreferredLocation, ConsolidateSameLot, ConsolidateSameSku, Proximity, NearestEmpty, NearestAvailable.Draft → Picking → Completed, z możliwością anulowania.FEFO (najpierw najkrótszy termin ważności), FIFO, LIFO, LeastQuantity oraz nadpisanie Manual.StockMovement, tworzony jako efekt uboczny przez zdarzenia domenowe. Daje to kompletną, tylko dopisywalną ścieżkę audytu.Admin, Manager, Worker. Wszystkie punkty końcowe domyślnie wymagają uwierzytelnienia, a operacje zapisu roli Admin lub Manager.Agregator ogłoszeń o pracę w Belgii. Zestaw scraperów zbiera oferty z pięciu portali, zapisuje je do wspólnej bazy z deduplikacją i pokazuje w jednym panelu z filtrami. Przebiegi startują automatycznie według harmonogramu ustawianego z poziomu aplikacji albo ręcznie, jednym przyciskiem.
Projekt zrobiony na zlecenie firmy i wdrożony na serwerze klienta (w chmurze OVHCloud) jako zestaw kontenerów Docker.
Backend to FastAPI (SQLAlchemy + Pydantic, SQLite, migracje Alembic). Frontend to aplikacja jednostronicowa (SPA) we Vue 3 z TypeScriptem i PrimeVue.
źródło:identyfikator z indeksem UNIQUE, więc powtórne przebiegi dopisują tylko nowe ogłoszenia.docker compose up. Baza i słownik stanowisk żyją w wolumenie, więc przebudowa obrazów ich nie narusza, a schemat bazy tworzą migracje wykonywane przy starcie.Szablon strony portfolio zbudowany na frameworku Angular. Aplikacja nie ma backendu: cała treść pochodzi z plików w katalogu public/assets, więc stworzenie własnego portfolio sprowadza się do ich podmiany, bez grzebania w kodzie źródłowym. Całość budowana jest jako strona statyczna (SSG), czyli każda podstrona w każdej wersji językowej trafia do gotowego HTML-a w czasie budowania i może być serwowana z CDN-u, bez żadnego serwera aplikacyjnego.
Dane strukturalne (profil, doświadczenie, projekty, technologie, ustawienia strony) opisane są w plikach JSON, a dłuższe opisy miejsc pracy i projektów w plikach Markdown, osobnych dla każdego języka.
outputMode: static generuje statyczny HTML dla każdej trasy i każdego języka. Wyszukiwarki i podglądy linków dostają gotową treść zamiast pustego <app-root>, a w przeglądarce strona hydratuje się do pełnej aplikacji SPA. Dane JSON i opisy Markdown pobrane w czasie budowania są osadzane w wynikowym HTML-u, więc przeglądarka nie pobiera ich drugi raz./pl, /en), a każda wersja językowa ma własny, prerenderowany HTML. Teksty interfejsu pochodzą z plików tłumaczeń (@ngx-translate), a lista dostępnych języków z konfiguracji strony. Przy wejściu na adres główny język dobierany jest z wcześniejszego wyboru użytkownika, a gdy go nie ma, z preferencji przeglądarki. Przełącznik w pasku nawigacji zmienia język bez przeładowania i bez opuszczania podstrony.canonical i komplet odnośników hreflang do swoich wersji językowych (razem z x-default), a atrybut lang w znaczniku <html> odpowiada wyświetlanej treści. Strona pobierania CV jest wyłączona z indeksowania.body, z płynnym przejściem kolorów. Wybór jest zapamiętywany i nakładany jeszcze przed pierwszym malowaniem strony, więc przy wejściu nie widać mignięcia drugiego motywu.tools/generate-thumbnails.py (Pillow, OpenCV) tworzy zoptymalizowane miniatury JPEG dla obrazów oraz z pierwszej klatki wideo, według konwencji nazw thumb_<nazwa>.jpg.technologies.json, a etykiety bez wpisu dostają kolor domyślny.IntersectionObserver; każdy element animuje się tylko raz.Aplikacja desktopowa do monitorowania czatu w grze Metin2. Czat odczytywany jest wyłącznie z obrazu: okno gry przechwytywane jest co 100 ms, a tekst rozpoznaje własny algorytm OCR działający na pikselach. Aplikacja nie ingeruje w proces gry, czyli nie modyfikuje plików klienta, nie czyta pamięci i nie wstrzykuje kodu.
Klient to Blazor Hybrid (.NET MAUI, .NET 10) z interfejsem opartym o MudBlazor. Przechwytywanie ekranu jest zaimplementowane dla Windows (P/Invoke user32/gdi32: GetDC + BitBlt na obszarze klienta wskazanego okna). Backendem jest ASP.NET Core Web API z bazą PostgreSQL.
[nazwa]) jako osobnych obiektów w wiadomościZamiast gotowego silnika OCR użyłem dopasowania wzorców bitowych, które dobrze pasuje do stałej, rastrowej czcionki gry:
charset.bmp + lista znaków w charset.txt) skanowana jest kolumnami. Każdy glif kodowany jest jako maska UInt128 i zapisywany w słowniku w obie strony (maska <-> znak).UInt128 (|= z przesunięciem bitowym), a pusta kolumna kończy znak i wyzwala odpytanie słownika.Serwery prywatne różnią się czcionką, kolorami czatu, formatem komunikatów, liczbą kanałów, godziną startu serwera oraz listą bossów (nazwa, liczba sztuk, czas respawnu).
{Channel}, {Player}, {Boss} i kompilowane do wyrażeń regularnych z grupami nazwanymi, z których wyciągane są dane o zabójstwie.Minimal API na ASP.NET Core (.NET 10), EF Core + PostgreSQL, walidacja przez FluentValidation.
Silnik do gier 2D napisany w C++ z graficznym edytorem: renderer OpenGL, Entity Component System (EnTT), symulacja fizyki (Box2D), natywne skryptowanie i własny moduł sieciowy oparty o GameNetworkingSockets.
Rdzeń architektury bazuje na otwartej wersji silnika Hazel, ale wprowadziłem w nim sporo zmian i własnych rozwiązań. Największe z nich to moduł sieciowy, napisany w całości przeze mnie. To projekt hobbystyczny na wczesnym etapie, nieprzeznaczony do produkcji komercyjnych gier; obecnie nie jest rozwijany.
Moduł sieciowy powstał na potrzeby mojej pracy magisterskiej. Badałem w niej, jak opóźnienia sieciowe wpływają na jakość symulacji rozgrywki, a dokładniej: jak różne wartości parametrów predykcji po stronie klienta przekładają się na częstotliwość i skalę desynchronizacji.
Projekt konfigurowany jest przez Premake5 (Windows, Visual Studio) i budowany w trzech konfiguracjach: Debug, Release (z edytorem) oraz Distribution, czyli wersji bez edytora, która pełni rolę runtime'u gry.
Silnik powstawał wcześniej pod nazwą Proton2D. Cała historia gita znajduje się w starym repozytorium https://github.com/Damian-Zuk/proton2d.
TextureAtlas, Sprite), skalowanie techniką 9-slice, animacje oparte na atlasie.Scene opakowuje rejestr EnTT. Entity to para identyfikatora i wskaźnika na scenę, a każdy obiekt dostaje UUID.AppScript > GameScript > EntityScript (logika aplikacji, trybu gry i pojedynczego obiektu), fabryka tworząca skrypty po nazwie klasy i makro rejestrujące.REGISTER_FIELD pojawiają się w inspektorze i są serializowane razem ze sceną. Obsługiwane typy to wartości skalarne, wektory, tekst i dowolne dane trywialnie kopiowalne.REPLICATED_FIELD jest automatycznie synchronizowane po sieci, opcjonalnie z funkcją wywoływaną po odebraniu nowej wartości.Ciała sztywne, kolidery prostokątne i okrągłe z parametrami materiału i filtrami kolizji, sensory, nasłuchiwanie kontaktów z wywołaniem zwrotnym w skrypcie oraz złączenia obrotowe (przypięcie obiektu do rodzica).
Architektura klient-serwer z w pełni autorytatywnym serwerem, w trybie listen server, dedykowanym lub klienta. Cała komunikacja idzie własnym binarnym strumieniem (nagłówek zapisywany jest wstecz po wyliczeniu rozmiaru ładunku), a każda odczytana wartość jest sprawdzana względem pozostałego rozmiaru bufora. Niespójny pakiet kończy się rozłączeniem klienta, a nie odczytem poza zakres.
PositionX, PositionY, ScaleX, ScaleY, Rotation), a zmiany pól skryptów wykrywane sumą kontrolną CRC32 liczoną per klient. Obiekt, w którym nic się nie zmieniło, jest wycofywany ze strumienia.Metodę synchronizacji ustawia się osobno dla każdego obiektu w inspektorze, razem z progami rekoncyliacji i teleportacji oraz czasem trwania korekty:
Narzędzie CLI w Pythonie, które generuje raport podatności (CVE) dla urządzeń IoT. Na wejściu przyjmuje ręcznie przygotowaną listę urządzeń w formacie JSON (model, wersja firmware'u, adres MAC) i/lub skany sieci zrobione Nmapem (XML). Dla każdego znalezionego elementu oprogramowania, czyli firmware'u, usług na otwartych portach i systemu operacyjnego, skrypt odpytuje bazę NIST NVD i dołącza znalezione podatności do raportu wyjściowego.
Projekt powstał w ramach przedmiotu „Bezpieczeństwo systemów IoT" na Politechnice Rzeszowskiej.
devices.json). Dane wpisane ręcznie są podstawą raportu i mają pierwszeństwo przed danymi ze skanów.nmap -A -oX). Hosty dopasowywane są do urządzeń z listy po adresie MAC, a nieznane trafiają do raportu jako nowe wpisy. Ze skanu wyciągane są: adres IPv4, producent (z MAC), porty w stanie open razem z usługami oraz wynik detekcji systemu operacyjnego.model + wersja, produkt + wersja, nazwa OS + rodzina + wersja). Identyfikatory w starszym formacie CPE 2.2 są zamieniane na CPE 2.3.cpeName. Jeśli dla urządzenia nie udało się ustalić CPE, wykonywane jest wyszukiwanie po słowach kluczowych, a CPE uzupełniane jest na podstawie pierwszej pasującej podatności.vulnerabilities.Raport to tablica urządzeń, a każdy wpis zawiera:
firmware, service (klucz: produkt wersja) i os (klucz: rodzina wersja). Każda kategoria ma łączną liczbę znalezionych CVE i ich listę, a pojedynczy wpis: identyfikator CVE, datę publikacji, opis, użytą metrykę CVSS, wynik punktowy i poziom istotności (LOW / MEDIUM / HIGH / CRITICAL).config.ini)linux, android). Dla nich liczba trafień jest tak duża, że wynik i tak nie byłby użyteczny, więc w raporcie zapisywana jest tylko adnotacja o pominięciu.Statyczna strona report-viewer.html, bez backendu i zależności. Plik JSON wczytuje się metodą przeciągnij i upuść. Urządzenia pokazywane są jako rozwijane panele, poziomy istotności mają swoje kolory, a identyfikatory CVE i CPE linkują do odpowiednich stron NVD. Ten sam widok obsługuje też plik wejściowy z listą urządzeń, bo ma on identyczną strukturę.
versionEndIncluding) nie są analizowane, więc podatność opisana zakresem może nie zostać wykryta.Projekt studencki, w którym Ansible automatyzuje konfigurację maszyn wirtualnych, instalację pakietów systemowych i wdrożenie prostej aplikacji LAMP.
Całość postawiłem w chmurze AWS: na osobnych instancjach działa węzeł zarządzający oraz zarządzane serwery webowe. Playbooki wykonywane są przez Ansible Semaphore, czyli graficzny interfejs dla Ansible. Pozwala on uruchamiać zadania z przeglądarki, ustawiać harmonogramy, trzymać klucze dostępowe w jednym miejscu i przeglądać historię wykonania razem z logami.
Dużą częścią projektu było udokumentowanie całej konfiguracji: od przygotowania instancji i dostępu SSH, przez instalację i konfigurację Semaphore, po opis działania poszczególnych ról. W repozytorium znajdują się tylko pliki Ansible: playbooki, role i inwentarz.
admin (z określonym UID i opisem).authorized_keys./etc/sudoers.memory_limit w konfiguracji PHP do 256 MB.posts, jeśli jeszcze nie istnieje./var/www, podmieniając pliki tylko wtedy, gdy wykryto zmiany.config.php z szablonu Jinja2, wstawiając do niego dane dostępowe do bazy i parametry strony.Aplikacja internetowa do tworzenia prostych ankiet jednopytaniowych i głosowania, wzorowana na platformie StrawPoll.com.
Backend to REST API na FastAPI (SQLAlchemy ORM, PostgreSQL, walidacja przez Pydantic) z Redisem, który trzyma stan sesji i liczniki limitów zapytań. Frontend to aplikacja jednostronicowa w React + TypeScript (Vite, Bootstrap). Całość uruchamiana jest przez Docker Compose, a dokumentacja API generowana automatycznie (Swagger).
Zamiast prostego wystawiania tokenów napisałem osobny moduł zarządzania sesjami (fastapi_jwt_redis), który integruje się z systemem zależności FastAPI:
jti, czasem ważności i czasem rozpoczęcia ważności.Aplikacja React z routingiem po stronie klienta i odświeżaniem sesji w tle (token dostępowy wymieniany jest, zanim wygaśnie). Pojedyncza ankieta ma własny adres, więc można się nią dzielić, a lista głosów oddanych przez zalogowanego użytkownika pobierana jest jednym zbiorczym zapytaniem dla wszystkich widocznych ankiet.
Sklep internetowy z częściami samochodowymi. Aplikację robiliśmy w pięcioosobowej grupie, w metodyce Scrum i we współpracy z grupą z wydziału zarządzania, w ramach zajęć projektowych z przedmiotu „Inżynieria Oprogramowania i Baz Danych".
Sklep zbudowany jest w Django (Python) z bazą PostgreSQL, szablonami po stronie serwera i interfejsem na Bootstrapie. Aplikacja jest przygotowana pod rynek polski, działa jako PWA i została wdrożona na Heroku (gunicorn, whitenoise).
Moja część pracy to katalog produktów i warstwa prezentacji: filtrowanie i sortowanie listy produktów, podział asortymentu na sekcje i kategorie razem z paskiem nawigacji, paginacja, obsługa cen promocyjnych z widokiem wyprzedaży i sekcją ofert specjalnych, a także przeniesienie zapytań widoku głównego do osobnej warstwy serwisowej. Po stronie interfejsu przygotowałem m.in. responsywny pasek nawigacji, pływający koszyk oraz szablony strony produktu, listy produktów, logowania, rejestracji i podsumowania zamówienia.
Oparta na open-source'owym module dla Django (lista porównywanych pozycji trzymana w sesji, dodawanie i usuwanie przez AJAX), który rozszerzyłem o zestawienie parametrów z pola JSON: atrybuty wszystkich porównywanych produktów łączone są w jeden zbiór i układane w tabelę wiersz po wierszu, a brakujące wartości dostają myślnik. Porównywać można produkty z jednej kategorii.
Gra logiczna wzorowana na klasycznym Sokobanie: celem jest wepchnięcie wszystkich skrzynek na wyznaczone pola. Napisana w C++17 z biblioteką SFML, bez gotowego silnika. Pętlę gry, system stanów, encje, animacje i kontrolki interfejsu zaimplementowałem od zera. Interfejs gry jest w języku polskim.
Projekt budowany jest przez CMake i przeznaczony na platformę Win32 (x86), zgodnie z dołączoną wersją SFML.
Nagłówek (18 bajtów) zawiera wymiary planszy, pozycję startową gracza i liczbę skrzynek. Dalej idzie tablica pozycji pól docelowych (po 8 bajtów) oraz siatka kafli zapisana po jednym bajcie na pole (brak kafla / podłoga / ściana / skrzynka).
Wariant tekstury podłogi i ściany losowany jest z ziarna wyliczonego z wymiarów poziomu. Dzięki temu plansza wygląda różnorodnie, a jednocześnie identycznie przy każdym wczytaniu.
Konsolowy komunikator klient-serwer na Windows, napisany w C++ na gniazdach Winsock (TCP), bez zewnętrznych bibliotek. Serwer i klient to dwie osobne aplikacje uruchamiane z linii poleceń. Cała logika (konta, rangi, uprawnienia, bany) siedzi po stronie serwera, a klient tylko wyświetla i przekazuje to, co wpisze użytkownik.
Jeden z moich wczesnych projektów hobbystycznych. Pisałem go, żeby nauczyć się programowania sieciowego i wielowątkowości.
srand zainicjowany kolejnymi znakami klucza i mieszanym z danymi operacją XOR.GUEST (niezarejestrowany), USER, VIP (kolory w wiadomościach), MOD i ADMIN, porównywane liczbowo. Komenda wymaga rangi nie niższej niż jej próg.7d, 12h, 30m), a termin wygaśnięcia zapisywany jest jako znacznik czasu. Po jego minięciu wpis przestaje obowiązywać, a pozostały czas pokazywany jest w czytelnej postaci.Zwykła konsola nie nadaje się do czatu, bo tekst przychodzący z sieci rozbija linię, którą użytkownik właśnie pisze. Na potrzeby projektu napisałem osobny komponent, który rozwiązuje ten problem:
{numer} osadzonymi w treści. Ten sam ciąg można wypisać z kolorami albo oczyścić ze znaczników, z czego korzystają m.in. wiadomości użytkowników rangi VIP.Klon gry Flappy Bird napisany w Pythonie z biblioteką Pygame. Pygame odpowiada tylko za okno, rysowanie i zdarzenia wejścia, a warstwę gry (obiekty, sceny, zasoby, pętlę aktualizacji) zbudowałem od zera.
z_index, co ustala kolejność warstw.tap (ekran startowy), playing, died i died_new_best, więc zmiana stanu gry sprowadza się do włączenia jednej sceny i wyłączenia pozostałych, zamiast ręcznego ukrywania pojedynczych elementów.Prędkość opadania narasta do wartości granicznej, a przyspieszenie zależy od fazy lotu: wznoszenie hamowane jest mocniej niż swobodny spadek, co daje charakterystyczny łuk skoku. Po zderzeniu ptak przechodzi w osobny tryb upadku z większym przyspieszeniem i wyższą prędkością graniczną, zatrzymuje się dopiero na poziomie ziemi, a ekran końcowy pojawia się z krótkim opóźnieniem.
Rekord przechowywany jest w pliku binarnym poprzedzonym skrótem SHA-256 liczonym z wyniku i stałego ciągu wpisanego w kod. Przy wczytywaniu skrót jest przeliczany ponownie i porównywany z zapisanym. Jeśli się nie zgadza (ktoś ręcznie edytował plik) albo plik jest uszkodzony, rekord jest odrzucany i liczenie zaczyna się od zera.
Gra platformowa 2D napisana w C++ z biblioteką SDL2. Biblioteka odpowiada tylko za okno, rysowanie tekstur i odczyt klawiatury, a resztę (pętlę gry, obiekty, fizykę, kamerę i wczytywanie map) napisałem od zera.
Kolizje nie polegają na cofaniu obiektu po wykryciu przenikania. Dla każdej osi z osobna przeglądane są obiekty leżące na drodze ruchu i wyznaczana jest najbliższa blokująca krawędź w kierunku prędkości, a nowa pozycja jest do niej przycinana. Dzięki temu obiekt nie przeskoczy przez przeszkodę nawet przy bardzo dużej prędkości, a granice mapy same zatrzymują ruch.
Grawitacja narasta wraz z masą obiektu aż do ustalonej prędkości granicznej. Kontakt z podłożem lub sufitem zeruje prędkość pionową i aktualizuje flagę przebywania w powietrzu, od której zależy skok (można skoczyć tylko stojąc na ziemi).
Ruch w poziomie rozpędza się od prędkości początkowej do maksymalnej. W powietrzu przyspieszenie jest niższe niż na ziemi, a prędkość maksymalna trochę wyższa, przez co bieganie i manewrowanie w skoku wyraźnie się od siebie różnią. Zmiana kierunku w locie lekko koryguje prędkość pionową, a osobny klawisz przenosi gracza z powrotem na punkt startowy.
Symulator ruletki zainspirowany platformą CSGODouble, działający w całości w przeglądarce: bez backendu, na czystym JavaScripcie z jQuery i Bootstrapem. Wczesny projekt z 2017 roku, z czasów, gdy uczyłem się podstaw JavaScriptu i tworzenia stron internetowych.
Koło ma 15 pól, a zakład stawia się na jeden z trzech kolorów:
Algorytm odtwarza schemat „provably fair" stosowany na platformach tego typu: wynik jest ustalony jeszcze przed rozpoczęciem losowania i można go zweryfikować po fakcie. Wygrywająca liczba wyliczana jest z trzech wartości:
seed – sekretny ciąg znaków, udostępniany następnego dnia, żeby można było sprawdzić prawidłowość losowań z dnia poprzedniego (generowany jako skrót losowego ciągu o długości 48–64 znaków),public – wartość publiczna, którą jest data bieżącego dnia w formacie rok-miesiąc-dzień,round – numer rundy w ramach danego dnia.Wartości łączone są w jeden ciąg i przepuszczane przez funkcję skrótu SHA-256. Pierwsze osiem znaków wyniku odczytywane jest jako liczba szesnastkowa, a reszta z jej dzielenia przez 15 wyznacza wygrywające pole. Z kolejnych znaków tego samego skrótu wyliczane jest też przesunięcie w obrębie pola, dzięki czemu wskaźnik zatrzymuje się za każdym razem w innym miejscu tego samego pola, również w sposób możliwy do zweryfikowania.
Pola ułożone są na zapętlonej taśmie w kolejności naprzemiennej (czerwone i czarne po obu stronach zera), a losowanie polega na przesuwaniu tła o wyliczony dystans. Ruch wyhamowuje wykładniczo: w każdym kroku pokonywana jest część pozostałej odległości, więc im bliżej celu, tym koło zwalnia łagodniej. Dystans startowy to kilka pełnych obrotów plus droga do wylosowanego pola, a tempo wyhamowania reguluje się suwakiem prędkości.
Osobny panel pozwala samemu sprawdzić działanie generatora: ręcznie ustawić lub wylosować ponownie seed, public i round, wyłączyć automatyczne zwiększanie numeru rundy, zmienić długość odliczania i prędkość animacji, znieść limit darmowych żetonów, a także uruchomić losowanie natychmiast, bez czekania na koniec odliczania. Wpisując te same trzy wartości, można odtworzyć dowolne wcześniejsze losowanie i potwierdzić, że wynik nie został po drodze zmieniony.