To jest wiadomość testowa potwierdzająca poprawną konfigurację poczty SMTP.
"); + return TestResultResponse.from(result); + } + + @PostMapping("/sms/test") + public TestResultResponse testSms(@Valid @RequestBody TestSendRequest request) { + SendResult result = smsSender.send( + mailConfigService.getSms(), + request.recipient().trim(), + "Test konfiguracji bramki SMS – Polska Lokalnie"); + return TestResultResponse.from(result); + } + + public record TestSendRequest(@NotBlank String recipient) { + } + + public record TestResultResponse(boolean ok, String providerMessageId, String error) { + static TestResultResponse from(SendResult result) { + return new TestResultResponse(result.ok(), result.providerMessageId(), result.error()); + } + } +} diff --git a/backend/src/main/java/pl/polskalokalnie/mailconfig/MailConfigService.java b/backend/src/main/java/pl/polskalokalnie/mailconfig/MailConfigService.java new file mode 100644 index 0000000..baa314a --- /dev/null +++ b/backend/src/main/java/pl/polskalokalnie/mailconfig/MailConfigService.java @@ -0,0 +1,91 @@ +package pl.polskalokalnie.mailconfig; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Dostep do jednorekordowej konfiguracji SMTP i bramki SMS. Sekrety (haslo, klucz API) sa + * nadpisywane tylko przy jawnej zmianie; puste pole w zadaniu zachowuje dotychczasowa wartosc. + * Metody get* zwracaja encje z sekretami wylacznie na uzytek wewnetrzny (sendery) - nie sa + * serializowane do klienta. + */ +@Service +public class MailConfigService { + + private static final long SINGLETON_ID = 1L; + + private final SmtpConfigRepository smtpConfigRepository; + private final SmsGatewayConfigRepository smsGatewayConfigRepository; + + public MailConfigService(SmtpConfigRepository smtpConfigRepository, + SmsGatewayConfigRepository smsGatewayConfigRepository) { + this.smtpConfigRepository = smtpConfigRepository; + this.smsGatewayConfigRepository = smsGatewayConfigRepository; + } + + @Transactional + public SmtpConfig getSmtp() { + return smtpConfigRepository.findById(SINGLETON_ID).orElseGet(() -> { + SmtpConfig config = new SmtpConfig(); + config.setId(SINGLETON_ID); + return smtpConfigRepository.save(config); + }); + } + + @Transactional + public SmsGatewayConfig getSms() { + return smsGatewayConfigRepository.findById(SINGLETON_ID).orElseGet(() -> { + SmsGatewayConfig config = new SmsGatewayConfig(); + config.setId(SINGLETON_ID); + return smsGatewayConfigRepository.save(config); + }); + } + + @Transactional + public SmtpConfig updateSmtp(SmtpConfigRequest request) { + SmtpConfig config = getSmtp(); + config.setHost(trimToNull(request.host())); + config.setPort(request.port()); + if (request.sslEnabled() != null) { + config.setSslEnabled(request.sslEnabled()); + } + config.setUsername(trimToNull(request.username())); + config.setFromName(trimToNull(request.fromName())); + config.setContactFormRecipient(trimToNull(request.contactFormRecipient())); + if (request.enabled() != null) { + config.setEnabled(request.enabled()); + } + // Haslo zmieniamy tylko gdy podane (niepuste) - inaczej zachowujemy dotychczasowe. + String password = request.password(); + if (password != null && !password.isBlank()) { + config.setPassword(password); + } + return smtpConfigRepository.save(config); + } + + @Transactional + public SmsGatewayConfig updateSms(SmsConfigRequest request) { + SmsGatewayConfig config = getSms(); + config.setEndpointUrl(trimToNull(request.endpointUrl())); + config.setCreator(trimToNull(request.creator())); + if (request.timeoutSeconds() != null) { + config.setTimeoutSeconds(request.timeoutSeconds()); + } + if (request.enabled() != null) { + config.setEnabled(request.enabled()); + } + String apiKey = request.apiKey(); + if (apiKey != null && !apiKey.isBlank()) { + config.setApiKey(apiKey); + } + return smsGatewayConfigRepository.save(config); + } + + private static String trimToNull(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } +} diff --git a/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsConfigRequest.java b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsConfigRequest.java new file mode 100644 index 0000000..7b76afa --- /dev/null +++ b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsConfigRequest.java @@ -0,0 +1,13 @@ +package pl.polskalokalnie.mailconfig; + +/** + * Zapis konfiguracji bramki SMS. Pole {@code apiKey} nadpisuje klucz tylko gdy niepuste. + */ +public record SmsConfigRequest( + String endpointUrl, + String apiKey, + String creator, + Integer timeoutSeconds, + Boolean enabled +) { +} diff --git a/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsConfigResponse.java b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsConfigResponse.java new file mode 100644 index 0000000..de6dafb --- /dev/null +++ b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsConfigResponse.java @@ -0,0 +1,23 @@ +package pl.polskalokalnie.mailconfig; + +/** + * Odpowiedz konfiguracji bramki SMS - bez klucza API. {@code apiKeySet} sygnalizuje jedynie, czy + * klucz jest zapisany (badge "ustawione"). + */ +public record SmsConfigResponse( + String endpointUrl, + String creator, + Integer timeoutSeconds, + boolean enabled, + boolean apiKeySet +) { + public static SmsConfigResponse from(SmsGatewayConfig config) { + return new SmsConfigResponse( + config.getEndpointUrl(), + config.getCreator(), + config.getTimeoutSeconds(), + config.isEnabled(), + config.getApiKey() != null && !config.getApiKey().isBlank() + ); + } +} diff --git a/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsGatewayConfig.java b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsGatewayConfig.java new file mode 100644 index 0000000..1357b88 --- /dev/null +++ b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsGatewayConfig.java @@ -0,0 +1,81 @@ +package pl.polskalokalnie.mailconfig; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +/** + * Konfiguracja bramki SMS (SoftSPM) - pojedynczy rekord (id = 1). Klucz API trzymany w bazie, + * nigdy zwracany w odpowiedziach API (maskowany) ani logowany. + */ +@Entity +@Table(name = "sms_gateway_config") +public class SmsGatewayConfig { + + @Id + private Long id = 1L; + + @Column(length = 400) + private String endpointUrl; + + @Column(length = 400) + private String apiKey; + + // Pole "creator" bramki - znacznik zrodla wiadomosci, max 50 znakow. + @Column(length = 50) + private String creator; + + private Integer timeoutSeconds = 10; + + @Column(nullable = false) + private boolean enabled = false; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getEndpointUrl() { + return endpointUrl; + } + + public void setEndpointUrl(String endpointUrl) { + this.endpointUrl = endpointUrl; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public Integer getTimeoutSeconds() { + return timeoutSeconds; + } + + public void setTimeoutSeconds(Integer timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsGatewayConfigRepository.java b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsGatewayConfigRepository.java new file mode 100644 index 0000000..cd2d90d --- /dev/null +++ b/backend/src/main/java/pl/polskalokalnie/mailconfig/SmsGatewayConfigRepository.java @@ -0,0 +1,6 @@ +package pl.polskalokalnie.mailconfig; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SmsGatewayConfigRepository extends JpaRepository" + + "Otrzymujesz tę wiadomość, ponieważ wyraziłeś zgodę na kontakt marketingowy. " + + "Zrezygnuj z otrzymywania wiadomości.
"; + return "" + message + "
Adres jest nieprawidłowy albo strona została przeniesiona.
+Narzędzia
@@ -4111,9 +4286,9 @@ function AdminPage({ onNavigate }: { onNavigate: (view: View) => void }) {Brak ogłoszeń do wyświetlenia.
@@ -4204,7 +4379,7 @@ function AdminPage({ onNavigate }: { onNavigate: (view: View) => void }) {Brak użytkowników do wyświetlenia.
@@ -4230,10 +4405,10 @@ function AdminPage({ onNavigate }: { onNavigate: (view: View) => void }) {Nasi eksperci pomogą Ci przejść przez proces zakupu i wynegocjować najlepszą cenę.
Nie masz jeszcze zapisanych wyszukiwań. Wejdź w Kup lub Wynajem i kliknij dzwonek „Zapisz wyszukiwanie”.
} - + Utwórz nowe wyszukiwanieWróć do swoich filtrów jednym kliknięciem i przejdź od razu do odpowiedniej listy ofert.
Ustaw filtry w Kup lub Wynajem i kliknij dzwonek „Zapisz wyszukiwanie”, a pojawią się tutaj.
- +Zarządzaj swoimi ogłoszeniami. Edytuj, promuj lub wstrzymuj oferty.
Zobacz, jak Twój profil prezentuje się publicznie dla innych użytkowników Mieszko.
{card.text}
+ Kompletny, uczciwy przewodnik po całym procesie zakupu – od pierwszej decyzji, przez budżet, + kredyt i formalności, aż po odbiór kluczy. Prostym językiem, z listami kontrolnymi i praktycznymi + wskazówkami, które pomogą Ci uniknąć najczęstszych błędów. +
+Cała droga w skrócie. Poniżej rozwijamy każdy etap dokładnie.
++ Zanim zaczniesz przeglądać oferty, odpowiedz sobie na kilka pytań. To one, a nie ładne zdjęcia, + zdecydują o trafności zakupu. Ustal, czy kupujesz dla siebie, dla rodziny, czy pod inwestycję, + jak długo planujesz tu mieszkać i co jest dla Ciebie nienegocjowalne – metraż, liczba pokoi, + piętro, winda, balkon czy miejsce postojowe. +
++ Rozdziel potrzeby od zachcianek. Potrzeba to np. druga sypialnia przy powiększającej się rodzinie. + Zachcianka to konkretny kolor kuchni, który i tak zmienisz. Im lepiej rozpoznasz swoje realne potrzeby, + tym mniej ofert będziesz musiał obejrzeć i tym rzadziej dasz się ponieść emocjom. +
+Wypisz na kartce trzy kryteria, których nie odpuścisz, i trzy, które są mile widziane. Wracaj do tej listy przy każdym oglądaniu – to najprostsza ochrona przed impulsywną decyzją.
++ Budżet to nie tylko cena mieszkania. To cena powiększona o wszystkie koszty transakcyjne oraz o to, + co wydasz zaraz po zakupie. Zsumuj cenę, podatki, taksę notarialną, wpisy do księgi wieczystej, + ewentualną prowizję pośrednika i koszty kredytu. Dopiero ta kwota pokazuje, na co naprawdę Cię stać. +
+| Koszt | +Kto ponosi | +Ile (orientacyjnie) | +Uwagi | +
|---|---|---|---|
| {row.name} | +{row.who} | +{row.value} | +{row.note} | +
Nie planuj zakupu na granicy możliwości. Poduszka finansowa na kilka rat, drobny remont i nieprzewidziane wydatki to nie luksus, tylko element bezpiecznego budżetu.
++ Zakup za gotówkę jest szybszy, prostszy i pozwala mocniej negocjować, bo sprzedający nie czeka na + decyzję banku. Nie zawsze jednak warto zamrażać wszystkie oszczędności w jednej nieruchomości – + tracisz płynność i poduszkę bezpieczeństwa. +
++ Kredyt hipoteczny rozkłada koszt w czasie i pozwala kupić wcześniej, ale oznacza wieloletnie + zobowiązanie, odsetki i zależność od zmian stóp procentowych. Zanim zdecydujesz, porównaj oferty + kilku banków i policz całkowity koszt kredytu, a nie tylko wysokość raty. +
++ Zdolność kredytowa to maksymalna kwota, jaką bank jest gotów Ci pożyczyć. Wpływają na nią głównie: + wysokość i stabilność dochodów, forma zatrudnienia, liczba osób na utrzymaniu, inne zobowiązania + (raty, karty, limity) oraz historia w BIK. Banki liczą też bufor na wypadek wzrostu stóp, dlatego + maksymalna kwota bywa niższa, niż wynikałoby z samej raty. +
++ Każdy bank liczy zdolność inaczej, więc warto sprawdzić kilka ofert – różnice bywają znaczne. + Zanim złożysz wnioski, uporządkuj finanse: spłać drobne zobowiązania, zamknij nieużywane karty + i limity, zadbaj o pozytywną historię kredytową. To realnie podnosi zdolność. +
+Skorzystaj z naszych kalkulatorów po prawej stronie, aby oszacować budżet i porównać koszty. Ostateczną zdolność zawsze potwierdza bank na podstawie dokumentów.
++ Rynek pierwotny to zakup od dewelopera – najczęściej nowe, energooszczędne mieszkanie, często jeszcze + w budowie i do wykończenia. Rynek wtórny to zakup od dotychczasowego właściciela – zwykle mieszkanie + gotowe do zamieszkania, w ukształtowanej okolicy. Oba mają swoje zalety; wybór zależy od tego, czy + wolisz nowe i „pod siebie”, czy gotowe i sprawdzone. +
++ Lokalizacji nie zmienisz remontem, dlatego to jedno z najważniejszych kryteriów. Sprawdź dojazd do + pracy i szkoły, dostęp do komunikacji, sklepów, przychodni i terenów zielonych. Odwiedź okolicę o + różnych porach dnia – rano, po południu i wieczorem – żeby poznać hałas, ruch i realny klimat miejsca. +
++ Zajrzyj do miejscowego planu zagospodarowania przestrzennego, żeby wiedzieć, co może powstać w + sąsiedztwie – park czy ruchliwa droga to duża różnica dla komfortu i przyszłej wartości. Zwróć uwagę + na kierunki świata i nasłonecznienie oraz na to, czy okolica się rozwija, czy raczej podupada. +
++ Nie kupuj po jednym oglądaniu i nie oglądaj tylko przy pełnym słońcu. Przyjdź kilka razy, także + wieczorem, i nie bój się zadawać pytań: o wysokość czynszu i co obejmuje, o koszty ogrzewania, o wiek + instalacji, o plany remontowe budynku i o powód sprzedaży. Rób zdjęcia i notatki, bo po obejrzeniu + kilku ofert łatwo je pomylić. +
++ Zwróć uwagę na to, czego nie widać na pierwszy rzut oka: ślady wilgoci w narożnikach i pod oknami, + stan okien i drzwi, ciśnienie wody, działanie wentylacji, wygłuszenie między mieszkaniami. Sprawdź też + części wspólne – klatkę, windę, piwnicę i otoczenie budynku mówią wiele o tym, jak zarządzana jest + nieruchomość. +
++ Stan techniczny decyduje o tym, ile dołożysz po zakupie. Najważniejsze punkty to instalacja + elektryczna (czy jest bezpieczna i o odpowiedniej mocy), instalacja wodno-kanalizacyjna i piony, + ogrzewanie, wentylacja oraz izolacja i ślady wilgoci lub grzyba. W starszych budynkach zwróć uwagę na + wiek instalacji i okien – ich wymiana bywa kosztowna. +
+Przy droższym zakupie lub starszym mieszkaniu warto zabrać na oględziny osobę znającą się na budownictwie. Koszt takiej konsultacji jest niewielki wobec potencjalnych ukrytych wad.
++ Księga wieczysta to dokument, który pokazuje stan prawny nieruchomości. Odpis możesz sprawdzić online, + znając numer księgi. Zwróć uwagę na cztery działy: oznaczenie nieruchomości, właściciela, prawa i + ograniczenia oraz hipoteki. To podstawowa ochrona przed zakupem mieszkania z niespodziankami. +
+Wpis obciążający nieruchomość może przejść na Ciebie razem z zakupem. Sprawdzenie księgi to kilka minut, a chroni przed poważnymi problemami.
++ Negocjacje zacznij od wiedzy. Sprawdź ceny podobnych mieszkań w okolicy, zobacz, jak długo oferta jest + wystawiona i poznaj sytuację sprzedającego. Argumentem nie są emocje, tylko fakty: konieczne remonty, + stan instalacji, wysoki czynsz czy niższe ceny porównywalnych lokali. +
++ Bądź konkretny i spokojny. Podaj kwotę, którą jesteś gotów zapłacić, wraz z uzasadnieniem, i miej + wyznaczoną granicę, powyżej której rezygnujesz. Gotowość do szybkiej, pewnej transakcji – zwłaszcza za + gotówkę – to często silniejszy argument niż sama presja na obniżkę. +
++ Umowa przedwstępna zabezpiecza ustalenia, zanim dojdzie do finalnej transakcji. Określa cenę, termin + zawarcia umowy właściwej, stan mieszkania i warunki, np. uzyskanie kredytu. Można ją zawrzeć zwykłą + formą pisemną lub u notariusza – forma notarialna daje silniejszą ochronę. +
+Zadatek działa dyscyplinująco: jeśli od umowy odstąpi kupujący, traci zadatek; jeśli sprzedający – zwraca go w podwójnej wysokości. Zaliczka jest po prostu zwracana. Dla bezpieczeństwa transakcji częściej stosuje się zadatek.
++ Przeniesienie własności mieszkania musi mieć formę aktu notarialnego. To notariusz sprawdza tożsamość + stron, stan prawny nieruchomości i treść umowy, a następnie składa wniosek o wpis nowego właściciela do + księgi wieczystej. Przy akcie płacisz taksę notarialną, podatek oraz opłaty sądowe. +
++ Przed podpisaniem dokładnie przeczytaj projekt aktu – ceny, terminy, sposób i datę wydania mieszkania, + zapisy o zadatku i o wykreśleniu ewentualnej hipoteki. Jeśli czegoś nie rozumiesz, pytaj notariusza. + To jego rola, żeby wyjaśnić Ci każdy zapis. +
++ Odbiór to moment sprawdzenia, czy dostajesz mieszkanie w umówionym stanie. Spisz protokół zdawczo-odbiorczy: + stany liczników (prąd, woda, gaz, ciepło), liczbę kluczy i pilotów oraz to, co zostaje w mieszkaniu. + Przy nowym lokalu od dewelopera dokładnie sprawdź jakość wykończenia i zgłoś wszystkie usterki w protokole. +
+Każdą usterkę i każdy stan licznika zapisz w protokole podpisanym przez obie strony. To Twój dowód na wypadek późniejszych rozliczeń i reklamacji.
++ Po odbiorze zostają formalności, o których łatwo zapomnieć w euforii. Przepisz na siebie umowy na prąd, + gaz, wodę i internet, zgłoś zmianę właściciela do wspólnoty lub spółdzielni, rozważ ubezpieczenie + mieszkania i zamelduj się pod nowym adresem. Zachowaj wszystkie dokumenty zakupu w jednym miejscu. +
+Wydrukuj lub pobierz w PDF i miej przy sobie podczas oglądania.
+Większości kosztownych pomyłek da się uniknąć, jeśli wie się, gdzie najczęściej się pojawiają.
+{mistake.text}
+{item.a}
++ Zakup mieszkania to jedna z ważniejszych decyzji finansowych w życiu, ale rozłożona na etapy przestaje + być przytłaczająca. Zacznij od potrzeb i budżetu, świadomie wybierz sposób finansowania, dokładnie + sprawdź stan techniczny i prawny, a formalności potraktuj poważnie. Najlepszą ochroną są wiedza, + spokój i pisemne ustalenia – emocje zostaw na moment odbioru kluczy. +
+Ten poradnik ma charakter informacyjny i nie stanowi porady prawnej ani podatkowej. Szczegóły dotyczące podatków, opłat i umów potwierdzaj u notariusza, doradcy kredytowego lub prawnika.
+Przeglądaj aktualne oferty dopasowane do Twoich potrzeb i budżetu.
+{topic.text}
- {topic.count} -{topic.text}
+ {topic.count} + > + ); + + return isBuying ? ( + + {content} + + ) : ( +Bezpłatna wycena online w 30 sekund
Sprawdź, jaką kwotę kredytu hipotecznego możesz uzyskać i jakie będą orientacyjne warunki. Wynik przelicza się na bieżąco przy każdej zmianie.
+{rating.note}
+
+
+ {activeCategory + ? activeCategory.text + : 'Sprawdzone firmy i specjaliści z całej Polski. Porównaj oferty, oceny i opinie klientów.'} +
+ + {companies.length === 1 ? '1 firma' : `${companies.length} firm`} + {activeCategory ? ` w kategorii „${activeCategory.title}”` : ' w bazie'} + +{step.text}
+{category.text}
- Zobacz firmy{[item.city, item.district].filter(Boolean).join(', ')}
{meta}