To jest pierwszy wpis z serii "Portfolio zrealizowanych aplikacji", w której pokazuję projekty, które zbudowałem od zera. Na pierwszy ogień idzie CMS Open — mój pierwszy w pełni autorski system zarządzania treścią, napisany w ASP.NET Core 3.1. Kod jest otwarty (github.com/jarmatys/CMS), strona projektu to cmsopen.net. Chciałbym tu opowiedzieć nie tylko co ten system robi, ale przede wszystkim — po co go w ogóle napisałem i czego mnie nauczył.
Po co mi był CMS od zera
Przez lata, jak większość freelancerów budujących strony dla klientów, sięgałem po WordPressa. Działał, ale im więcej projektów prowadziłem, tym bardziej dochodziłem do wniosku, że płacę za to niewidoczny podatek: pluginy nagryzające się na siebie, aktualizacje rozwalające template, utrzymanie, które zjada czas, i niezbyt komfortowe wdrażanie wersji roboczych. Do tego pisałem wtedy komercyjnie w .NET i strasznie raziła mnie myśl, że na własnym stacku nie umiem dostarczyć klientowi kompletnego produktu contentowego.
Stąd pomysł: napiszę sobie własny, lekki CMS, dokładnie pod te scenariusze, na które najczęściej natrafiam — blog firmowy, proste strony contentowe, formularz kontaktowy, zarządzanie mediami, integracje z Google Analytics czy Pixelem. Potraktowałem to jako poligon — chciałem sprawdzić, czy jako pojedynczy developer jestem w stanie domknąć coś od "pustego repo" do "klient wchodzi w panel i sobie edytuje wpis".
Najlepszy sposób, żeby naprawdę zrozumieć, jak działa WordPress — to napisać swój własny WordPress. Wtedy okazuje się, ile tam jest drobnych decyzji, których się wcześniej nie zauważało.
Stos technologiczny
Wybór technologii był dla mnie dość naturalny. Pisałem w tym, w czym czułem się najpewniej, plus parę rzeczy świadomie dorzuciłem, żeby się czegoś nowego nauczyć.
- ASP.NET Core 3.1 MVC — warstwa web, routing, widoki Razor dla frontu bloga i panelu admina
- Entity Framework Core — ORM, migracje, obsługa bazy
- SQL Server — baza relacyjna jako główne źródło prawdy dla treści
- ASP.NET Core Identity — logowanie, zarządzanie użytkownikami, role i uprawnienia
- Cloudinary — zewnętrzny storage dla zdjęć i mediów; świadomie nie chciałem trzymać plików na serwerze aplikacji
- Edytor WYSIWYG — do pisania treści wpisów i stron z formatowaniem (nagłówki, zdjęcia wyróżniające, zajawki)
- SMTP + własna obsługa poczty — formularz kontaktowy i powiadomienia o nowych komentarzach
- SEO + redirecty — zarządzanie meta tagami i mapa przekierowań 301 dla migracji z innych systemów
Decyzja o Cloudinary była jedną z lepszych, jakie podjąłem w tym projekcie. Dzięki temu sam hosting aplikacji zrobił się lekki — nie musiałem martwić się o wolumeny na dysku, backupy plików ani o to, co się stanie, gdy klient wrzuci 4 GB zdjęć.
Co można zrobić w panelu admina
CMS Open celuje w coś w rodzaju "WordPress, tylko po mojemu" — zestaw funkcjonalności, który realnie pokrywa 80% potrzeb małej strony firmowej z blogiem. W filmie pokazuję to klikając na żywo, tu zrobię to w formie tabeli, żeby było łatwiej się zorientować, co dokładnie jest w środku.
| Obszar | Funkcjonalność |
|---|---|
| Wpisy (posts) | Tworzenie, edycja, szkice, automatyczny slug, zdjęcie wyróżniające, zajawka, data publikacji |
| Kategorie | Dynamiczne dodawanie, przypisywanie wielu kategorii do wpisu, filtrowanie po kategorii na froncie |
| Komentarze | Moderacja (akceptacja/odrzucenie), edycja treści, globalne i per-wpis wyłączenie komentowania |
| Media | Biblioteka mediów, edycja tytułu i atrybutu alt, podgląd szczegółów, hosting w Cloudinary |
| Strony statyczne | Osobny model od wpisów — tytuł, treść, opis SEO, słowa kluczowe, flagi noindex / canonical |
| Użytkownicy | Lista, tworzenie kont, nadawanie uprawnień, zmiana haseł, statystyki (liczba wpisów per user) |
| Formularz kontaktowy | Pobiera ustawienia SMTP z panelu, wysyła wiadomości, zapisuje zgłoszenia w bazie |
| Powiadomienia | Maile o nowych komentarzach, gdy poczta jest skonfigurowana |
| SEO | Meta-tagi per-strona, dynamiczne własne meta, integracja Google Analytics / Maps / Pixel / reCAPTCHA |
| Przekierowania 301 | Dedykowana zakładka — mapa "stary URL → nowy URL" z kodami odpowiedzi dla Google |
| Social media | Dodawanie dowolnej liczby linków z ikonkami — generowane dynamicznie w stopce i nagłówku |
| Polityka prywatności | Edytowana w panelu, wyświetlana dynamicznie przy formularzu kontaktowym |
| Ustawienia globalne | Liczba wpisów na stronę (pagination), globalne włączenie/wyłączenie komentarzy, konfiguracja poczty |
| Zgłoszenia do dev | Wbudowany formularz "zgłoś błąd / potrzebę" wysyłający ticket do web developera utrzymującego system |
Tym ostatnim byłem dość zadowolony — pomysł polegał na tym, że jeśli wdrażam CMS u klienta, to klient nie musi szukać mojego maila, gdy coś nie działa. Klika znak zapytania w panelu, pisze co chce, a ja dostaję ustrukturyzowane zgłoszenie. Mała rzecz, ale realnie skracała komunikację.
Czego się nauczyłem
Projekty portfolio są wartościowe nie przez to, że kod lśni, tylko przez to, że po miesiącach pracy widać wyraźnie, co się zepsuło, gdzie się za bardzo nadwyrężyło i co zadziałało. Kilka rzeczy, które z tego projektu wyniosłem:
1. Model danych to fundament. Najwięcej późniejszych problemów brało się stąd, że na początku za słabo przemyślałem relacje — zwłaszcza między wpisami, kategoriami i mediami. Każda zmiana schematu po paru miesiącach kosztowała więcej migracji, niż się spodziewałem. Dzisiaj zacząłbym od diagramu ERD, a nie od pierwszego DbContext.
2. Zewnętrzne storage to czysty zysk. Przeniesienie mediów do Cloudinary sprawiło, że aplikacja jest lekka, tania w hostingu i przenośna. Gdybym wtedy wrzucił wszystko do wwwroot/uploads/, dziś bym to pewnie nadal spłacał.
3. Panel admina wymaga osobnego UX. Pierwsze wersje panelu pisałem jak "jeszcze jeden widok MVC". Szybko zobaczyłem, że panel admina to w zasadzie osobna aplikacja — z innymi wzorcami, innym tempem pracy i innymi oczekiwaniami użytkownika. Dzisiaj zrobiłbym go jako SPA (React lub Blazor) z API, a nie jako Razor Pages mieszane z publicznym frontem.
4. Feature flags ratują życie. Globalne wyłączanie komentarzy, toggle na powiadomienia, flagi noindex per-strona — te drobne przełączniki okazały się nieproporcjonalnie ważne, gdy trzeba było coś szybko wyłączyć u klienta na produkcji.
5. Najtrudniejsze jest dokończenie. Napisanie 70% CMS-a zajęło mi sensowny czas. Ostatnie 30% — moderacja komentarzy, redirecty, integracje, SEO, mailing — zajęło mniej więcej tyle samo, co cała reszta. Teraz już wiem, że ta asymetria to norma, a nie wyjątek.
Gdybym pisał to dziś, zrobiłbym trzy rzeczy inaczej: headless architecture (API + osobny front w Next.js), .NET w wersji LTS (8) zamiast 3.1 i od początku testy integracyjne na kluczowych flow (logowanie, publikacja wpisu, wysłanie formularza).
Mimo tych "zrobiłbym inaczej" — CMS Open poszedł na produkcję, obsłużył kilka realnych klientów i nauczył mnie więcej o produktowym podejściu do kodu niż jakikolwiek kurs. I to jest właściwie główny powód, dla którego warto budować własne rzeczy od zera: kończysz projekt z głową pełną pytań, na które nigdy byś nie wpadł, pisząc tylko ticket po tickecie.
Zobacz więcej
- Film na YouTube — prezentacja panelu admina w akcji (14 min)
- CMS Open na GitHubie — źródła systemu na licencji open source
- cmsopen.net — strona projektu
- Więcej wpisów z serii portfolio: armatys.me/wpisy
![🏋 Mierzenie ruchu klienta w klubie fitness - Agentfitness.pl [ Prezentacja aplikacji ]](https://i.ytimg.com/vi/IBkLeCzwMlc/hqdefault.jpg)
![🔍 Aplikacja do analizowania ogłoszeń motoryzacyjnych - Vehicleanalizer.com [ Prezentacja aplikacji ]](https://i.ytimg.com/vi/ZoAgnD39M2I/hqdefault.jpg)
![⭐ Skracarka linków - Short.dog [ Prezentacja aplikacji ]](https://i.ytimg.com/vi/imMhkA2GzRA/hqdefault.jpg)
![🚗 Platforma marketplace - Lendly.pl [ Prezentacja aplikacji ]](https://i.ytimg.com/vi/SgaE03kOtyw/hqdefault.jpg)