CMS Open — mój pierwszy system CMS w .NET Core 3.1

Case study CMS Open — autorskiego systemu zarządzania treścią w .NET Core 3.1. Po co go pisałem, co umie panel admina i czego nauczyłem się po drodze.

JAJarosław Armatys 6 min czytania
Wersja wideo tego wpisu — obejrzyj na YouTube

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.

ObszarFunkcjonalność
Wpisy (posts)Tworzenie, edycja, szkice, automatyczny slug, zdjęcie wyróżniające, zajawka, data publikacji
KategorieDynamiczne dodawanie, przypisywanie wielu kategorii do wpisu, filtrowanie po kategorii na froncie
KomentarzeModeracja (akceptacja/odrzucenie), edycja treści, globalne i per-wpis wyłączenie komentowania
MediaBiblioteka mediów, edycja tytułu i atrybutu alt, podgląd szczegółów, hosting w Cloudinary
Strony statyczneOsobny model od wpisów — tytuł, treść, opis SEO, słowa kluczowe, flagi noindex / canonical
UżytkownicyLista, tworzenie kont, nadawanie uprawnień, zmiana haseł, statystyki (liczba wpisów per user)
Formularz kontaktowyPobiera ustawienia SMTP z panelu, wysyła wiadomości, zapisuje zgłoszenia w bazie
PowiadomieniaMaile o nowych komentarzach, gdy poczta jest skonfigurowana
SEOMeta-tagi per-strona, dynamiczne własne meta, integracja Google Analytics / Maps / Pixel / reCAPTCHA
Przekierowania 301Dedykowana zakładka — mapa "stary URL → nowy URL" z kodami odpowiedzi dla Google
Social mediaDodawanie dowolnej liczby linków z ikonkami — generowane dynamicznie w stopce i nagłówku
Polityka prywatnościEdytowana w panelu, wyświetlana dynamicznie przy formularzu kontaktowym
Ustawienia globalneLiczba wpisów na stronę (pagination), globalne włączenie/wyłączenie komentarzy, konfiguracja poczty
Zgłoszenia do devWbudowany 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

Tagi:#portfolio#dotnet#cms#aspnet#case-study

Masz pytanie do tego wpisu?

Napisz do mnie — chętnie porozmawiam o szczegółach technicznych, albo o tym jak zastosować to u Ciebie w projekcie.