OpeNotes — aplikacja do notatek z Google OAuth
Projekt Full-Stack · React · Node.js · PostgreSQL · Google OAuth

OpeNotes — notatnik z logowaniem przez Google

Wieloużytkownikowa aplikacja SPA do tworzenia, edytowania i usuwania notatek. Rejestracja i logowanie e-mailem (bcrypt, Passport.js) oraz jednym kliknięciem przez Google OAuth 2.0. Notatki zapisane w PostgreSQL, stan zalogowania utrzymywany przez sesje serwerowe z httpOnly cookie.

React 19 Vite React Router 7 Node.js + Express PostgreSQL Passport.js Google OAuth 2.0 Material UI

01Wygląd aplikacji

Estetyka Google Keep — żółty header (#F5BA13), białe karty notatek na szachownicowym tle, Montserrat jako font, logo z ikoną żarówki (MUI TungstenIcon). Cztery oddzielne widoki połączone w SPA przez React Router.

📝

Your personal notes app

Place where you can keep your notes organized

Register
Login

strona główna — widok dla niezalogowanych (/)

ekran logowania — email/hasło + Google OAuth (/login)

Logout
+

Shopping list

Milk, eggs, bread, coffee, butter, apples

🗑

Book ideas

Atomic Habits, Deep Work, The Pragmatic Programmer…

🗑

Workout plan

Mon: chest · Wed: legs · Fri: back + arms

🗑

panel notatek — formularz create + karty z edycją i usuwaniem (/notebook)

Logout
Edit note
Title
Shopping list
Content
Milk, eggs, bread, coffee, butter, apples
Cancel
Save

dialog edycji — MUI Dialog z TextField fullWidth (/notebook)

02Funkcjonalności

  • Rejestracja — walidacja frontendowa (email format, min. 6 znaków, zgodność haseł) + walidacja po stronie serwera + bcrypt hash z 10 rundami soli. Po rejestracji auto-login przez req.login().
  • Logowanie e-mailem — Passport Local Strategy, bcrypt.compare(), sesja serwerowa httpOnly. Rate limiter: max 3 próby / 15 minut (kody HTTP 429).
  • Logowanie Google — OAuth 2.0 przez passport-google-oauth2. Jeśli e-mail istnieje — logowanie, jeśli nie — automatyczna rejestracja.
  • CRUD notatek — dodawanie, wyświetlanie, edycja przez MUI Dialog, usuwanie. Optimistic UI update przy dodawaniu i usuwaniu (brak reload strony), pełny reload po edycji.
  • Persystencja sesjiGET /api/check-session wywoływany przy starcie App, przywraca stan zalogowania po odświeżeniu strony.
  • Ochrona routów — React Router z Navigate: zalogowany → przekierowanie z /login na /notebook; niezalogowany → z /notebook na /login.

03Architektura — SPA + REST API

Klasyczna architektura rozdzielonego frontendu i backendu. React SPA (port 5173) komunikuje się z Express REST API (port 3000) przez Axios z withCredentials: true — to kluczowe dla sesyjnych cookies w środowisku cross-origin.

React SPA (Vite :5173)           Express API (:3000)          PostgreSQL
        │                               │                              │
        │ axios.get('/api/check-session')│                              │
        │ withCredentials: true ────────▶│                              │
        │                               │ req.isAuthenticated()         │
        │◀─────────────── {loggedIn: true, user} ──────────────────────│
        │                               │                              │
        │ axios.get('/api/shownotes') ──▶│ SELECT FROM notes            │
        │                               │ WHERE userid = $1 ───────────▶│
        │◀─────────────── {data: [...notes]} ◀─────────────────────────│
        │                               │                              │
        │ axios.post('/api/postnote') ──▶│ INSERT INTO notes            │
        │                               │ VALUES (...) ────────────────▶│
        │◀─────────────── 201 Created ◀─────────────────────────────────│

04Autentykacja — Passport + sesje + OAuth

Trzy ścieżki autentykacji obsługiwane przez Passport.js w jednym pliku backendowym.

// Sesja httpOnly — niedostępna dla JavaScript w przeglądarce
app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: {
    httpOnly: true,
    sameSite: "lax",
    maxAge: 60 * 60 * 1000   // 1 godzina
  }
}));

// Rate limiter: blokada po 3 nieudanych próbach / 15 minut
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 3,
  message: { message: "Too many login attempts. Try again later." }
});

// Google OAuth — auto-rejestracja jeśli email nie istnieje w bazie
const result = await db.query("SELECT * FROM users WHERE email = $1", [profile.email]);
if (result.rows.length === 0) {
  const newUser = await db.query(
    "INSERT INTO users (email, password) VALUES ($1, $2) RETURNING *",
    [profile.email, "google"]  // placeholder hasła dla kont OAuth
  );
  return done(null, newUser.rows[0]);
}

Frontend obsługuje kody HTTP z API: status 401 → “Invalid credentials”, status 429 → “Too many login attempts. Try again later.” — komunikaty błędów przekazywane z backendu do stanu errors w komponencie.

05Architektura React

Aplikacja zbudowana jako SPA z 8 komponentami funkcyjnymi. Stan zalogowanego użytkownika (user) przechowywany w App.jsx i przekazywany propami do komponentów. Hooki useState + useEffect do lokalnego stanu i efektów ubocznych.

  • CreateArea — rozwijany formularz (collapsed → expanded) z animacją MUI Zoom. Pole tytułu pojawia się dopiero po kliknięciu textarea. Floating Action Button z ikoną + do zapisu.
  • Note — karta z przyciskami MUI DeleteIcon i ModeEditIcon. Kliknięcie edycji ustawia isEditing: true, co otwiera dialog.
  • NoteEditorDialog — MUI Dialog fullWidth z TextField dla tytułu (single line) i treści (multiline 4 rows). Lokalny stan title/content synchronizowany z propsami przez useEffect([open, note]).
  • Notebook — pobiera notatki przez Axios przy useEffect([], []) (raz po mount). Optimistic update: po dodaniu notatki setUserNotes(prev => [...prev, newNote]) bez dodatkowego fetcha.
// CreateArea — expand on click, Zoom animation on FAB
function CreateArea(props) {
  const [isExpanded, setExpanded] = useState(false);

  return (
    <form className="create-note">
      {isExpanded && <input name="title" placeholder="Title" />}  {/* pojawia się po kliknięciu */}
      <textarea
        onClick={() => setExpanded(true)}   {/* kliknięcie → expand */}
        rows={isExpanded ? 3 : 1}
        placeholder="Take a note..."
      />
      <Zoom in={isExpanded}>           {/* FAB pojawia się z animacją */}
        <Fab onClick={submitNote}><AddIcon /></Fab>
      </Zoom>
    </form>
  );
}

06REST API — endpointy

POST
/register
Rejestracja — walidacja pól, bcrypt hash, auto-login przez req.login(), zwraca user object
POST
/login
Logowanie — Passport local, rate limited (3/15min), sesja httpOnly cookie
GET
/auth/google
Inicjalizacja Google OAuth 2.0 — redirect do Google consent screen
GET
/auth/google/notes
Callback OAuth — auto-rejestracja lub logowanie, redirect do /notebook
GET
/api/check-session
Sprawdzenie sesji przy starcie SPA — zwraca {loggedIn, user}
GET
/logout
Wylogowanie — req.logout() + session.destroy()
GET
/api/shownotes
Notatki zalogowanego użytkownika — SELECT WHERE userid = $1
POST
/api/postnote
Dodanie notatki — INSERT INTO notes, zwraca status 201
PATCH
/api/edit
Edycja tytułu i treści po noteidUPDATE notes SET title, content
DELETE
/api/delete
Usunięcie notatki po id z body — DELETE FROM notes WHERE noteid = $1

07Stack technologiczny

React 19
SPA — komponenty funkcyjne, useState, useEffect, props. Vite jako build tool z HMR.
React Router 7
Routing — 4 ścieżki z ochroną (Navigate): /, /login, /register, /notebook.
Axios
HTTP client — wszystkie wywołania z withCredentials: true dla cookies cross-origin.
Material UI 6
Komponenty — Dialog, TextField, Fab, Zoom, TungstenIcon, DeleteIcon, ModeEditIcon.
FontAwesome
Ikony — faGoogle (przycisk Google login), faNoteSticky (hero strony głównej).
Express.js
Backend — REST API, middleware (session, cors, passport, rate-limit, body-parser).
Passport.js
Autentykacja — local strategy (bcrypt) + google-oauth2 strategy w jednej konfiguracji.
PostgreSQL + pg
Baza danych — tabele users i notes, zapytania z parametrami pozycyjnymi $1, $2…
Bootstrap 5
Layout — klasy grid i kart na stronach logowania i rejestracji.

08Czego dowodzi ten projekt

Kompletny SPA

React Router z ochroną routów, session check przy starcie, optimistic updates — pełny cykl SPA.

Passport multi-auth

Local strategy (bcrypt) + Google OAuth 2.0 w jednym serwisie, auto-rejestracja przez OAuth.

Sesje + cross-origin

httpOnly cookie między :5173 a :3000 z CORS credentials: true — klasyczna architektura dev.

Pełne CRUD

GET, POST, PATCH, DELETE — wszystkie metody HTTP z proper status codes (201, 401, 404, 429, 500).

MUI Dialog pattern

Controlled dialog z lokalnym stanem synchronizowanym przez useEffect — typowy wzorzec React UI.

Punkt wyjścia

OpeNotes to baza, na której zbudowany jest Minds AIssistant — ten sam stack plus AI coach i kalendarz.

Zobacz kod na GitHubie

Repozytorium zawiera pełny frontend React i backend Express z Passport.js, Google OAuth i PostgreSQL.

Zobacz kod na GitHub
· · ·
Udostępnij jeśli spodobał Ci się mój projekt