Node.js, Express və MongoDB İstifadə Edərək Xəbər Oxuma Platforması Hazırlamaq - Bölüm 3

Node.js, Express və MongoDB İstifadə Edərək Xəbər Oxuma Platforması Hazırlamaq - Bölüm 3

26.08.2023

1216

Node.js, Express və MongoDB İstifadə Edərək Xəbər Oxuma Platforması Hazırlamaq - Bölüm 3

 

Xoş gəldiniz! Bu bölmədə biz layihəmizə istifadəçi qeydiyyatı, profilinə daxil ola bilməsi, istifadəçilərə xəbər yaratmağa icazə verilməsi kimi funksionallıqları əlavə edəcəyik. Bundan əlavə, istifadəçi vebsayta daxil olduqda navbar'ı uyğun şəkildə dəyişdirəcəyik.

 

  1. İstifadəçilərin idarə olunması

    Əvvəlki bölümdə yaratmış olduğumuz istifadəçilərin qeydiyyatdan keçməsi, daxil olması üçün səhifələrə əlavələr olunacaq. Biz həmçinin verilənlər bazasında istifadəçiləri idarə etmək üçün `Passport.js`-dən istifadə edəcəyik. İstifadəçi profillərinə baxmaq və onlardan çıxmaq da bu bölmədə müzakirə olunacaq mövzulardan biri olacaq.

     

  2. Navbar tənzimləmələri

    İstifadəçi daxil olubsa, “Xəbər Yaz” düyməsi ilə navbar'ı yeniləyəcəyik. Əlavə olaraq, əgər istofadəçi sistemdən çıxsa, o "Daxil ol" və "Qeydiyyat" düymələrinə yönləndirəcəyik. Bu yolla istifadəçilər xəbər yazma icazəsi yalnız daxil olan istifadəçilərə veriləcək.

     

  3. Xəbərlərin yaradılması

    Layihəmizə xəbər yaratma funksionallığı əlavə edəcəyik. İstifadəçilər sadə form vasitəsilə xəbər başlıqlarını və məzmununu daxil edə biləcəklər. Bu məlumat verilənlər bazasında saxlanılacaq və ana səhifədə digər istifadəçilərə göstəriləcək.
     

Bu bölümdə ələ alacağımız mövzular haqqında qısa məlumatdan sonra növbə ilə həmin hissələri işləməyə başlayaq!
 

Əvvəlcə istifadəçi modelini yaratmaqla başlayaq. Sonra, `Passport.js` ilə bu modeldən istifadə edərək istifadəçi qeydiyyatını və girişini həyata keçirəcəyik. Bu və əlavə bir neçə paketdən istifadə üçün əvvəlcə həmin paketləri yükləməliyik. `Visual Studio`-dan istifadə ediriksə, `ctrl + ~` və ya `Terminal -> New Terminal` klik edib `npm install express-session passport passport-local passport-local-mongoose` komandasını daxil edirik. Bizə lazım olan paketləri yüklədiyimizə görə istifadəçi modelini yarada bilərik:
 

models/user.js
 

javascript

import { Schema, model } from "mongoose";

import passportLocalMongoose from "passport-local-mongoose";

 

const userSchema = new Schema({

  username: String,

  email: String,

  password: String,

});

 

userSchema.plugin(passportLocalMongoose);

 

export const userModel = model("user", userSchema);

 

 

Bu modeldə biz `passport-local-mongoose` npm paketin istifadə edərək Passport.js-ə uyğun istifadəçi idarəetməsini təmin edirik. Bu paket istifadəçi adı və şifrə məlumatlarından istifadə edir və avtomatik olaraq şifrələmə və yoxlamanı idarə edir. Pasport.js üçün bəzi konfiqurasiyaları əlavə etmək üçün `config/passport.js` faylını yaradıb kodları əlavə edək:
 

config/passport.js

 

javascript

import passport from "passport";

import { Strategy } from "passport-local";

import { userModel } from "../models/user.js";

 

// Pasport konfiqurasiyası

passport.use(new Strategy(userModel.authenticate()));

passport.serializeUser(userModel.serializeUser());

passport.deserializeUser(userModel.deserializeUser());

 

export default passport;

 

 

Bu konfiqurasiyadan əlavə proqrama session` əlavə olunmalıdır. Session istifadəçilərə müəyyən müddət ərzində müəyyən məlumatlara daxil olmağa imkan verən veb proqramlardakı mexanizmdir. Bu, istifadəçinin brauzeri ilə veb server arasında saxlanılan məlumat anbarıdır. `Session` istifadəçinin məlumatlarını və digər müvəqqəti məlumatlarını saxlayır. express-session modulu Express.js-də sessiyanın idarə edilməsi üçün istifadə olunur. Aşağıda konfiqurasiya faylında `express-session` modulunun necə istifadə oluna biləcəyinə dair bir nümunə verilmişdir:

 

config/session.js;

 

javascript

import session from "express-session";

 

const sessionConfig = {

  secret: "secretkey", // Təhlükəsizlik məqsədləri üçün istifadə olunur, daha təhlükəsiz dəyərdən istifadə olunmalıdır.

  resave: false,

  saveUninitialized: false,

  cookie: {

    maxAge: 1000 * 60 * 60, // Session müddəti (millisaniyələrlə), məsələn, bir saat

    httpOnly: true,

  },

};

 

export default session(sessionConfig);

 

 

Son olaraq istifadəçinin giriş edib-etmədiyini bilmək üçün middleware'ə ehtiyyacımız olacaq. Bunun üçün `middleware` qovluğu və içərisində `auth.js` faylını yaradırıq. Daha sonra kodlarımızı əlavə edirik:

 

middleware/auth.js

 

javascript

const authMiddleware = (req, res, next) => {

  res.locals.currentUser = req.user;

  next();

};

 

export default authMiddleware;

 

Yuxarıdakı kod parçasında `client`-ə göndərilən `currentUser` dəyərini daha sonra navbar'ı tənzimləmək üçün və digər funksionallıqları əlavə etmək üçün istifadə edəcəyik. Session üçün də konfiqurasiyanı etdikdən sonra `index.js` faylına daxil oluruq. Burada son yazdığımız funksiyaların səhifə marşrutlardan(route) yuxarıda istifadə olunmasına diqqət olunmalıdır: 

 

javascript

// Digər kodlar

import session from "./config/session.js";

import passport from "./config/passport.js";

import authMiddleware from "./middleware/auth.js";

 

...

// Middleware

app.use(bodyParser.urlencoded({ extended: false }));

app.use(bodyParser.json());

app.use(express.static(path.join(__dirname, "public")));

 

// Session middleware

app.use(session);

 

// Passport middleware

app.use(passport.initialize());

app.use(passport.session());

 

app.use(authMiddleware);

 

 

İstifadəçi Qeydiyyatı və Girişi

 

İstifadəçilərin qeydiyyatdan keçməsi və daxil olması kimi funksionallıqları əlavə etmək məqsədilə `controllers/user.js` faylına kodlarımızı əlavə edirik:

 

controllers/user.js

 

javascript

export const registerHandler = (req, res, next) => {

  // Burada qeydiyyat əməliyyatları həyata keçirilir

  // Məsələn, req.body məlumatlarından istifadə edərək yeni istifadəçi yarada bilərsiniz

 

  const { username, email, password } = req.body;

  const newUser = new userModel({ username, email });

  userModel.register(newUser, password, (err) => {

    if (err) {

      return next(err);

    }

    res.redirect("/sing-up");

  });

 

  export const loginHandler = passport.authenticate("local", {

    // Burada giriş əməliyyatları yerinə yetirilir

 

    successRedirect: "/",

    failureRedirect: "/sign-up",

    failureFlash: true,

  });

};

 

Bu funksiyalar istifadəçi qeydiyyatını və Passport.js ilə girişi idarə edir. passport.authenticate() funksiyası istifadəçinin məlumatlarını yoxlayır və uğurlu giriş edildikdə istifadəçini yönləndirir.

 

Navbar tənzimləmələri
 

İstifadəçi sayta uğurla daxil olduqdan sonra navbar'da müəyyən dəyişiklikləri etmək üçün `views/partials/header.ejs` faylına keçid edib əvvəldən yazmış olduğumuz kodları yeniləyirik:

 

views/partials/header.ejs

 

html

<header class="header">

  <h1 class="display-4 text-center">Xəbər oxuma platforması</h1>

 

  <nav class="navbar navbar-expand-lg navbar-light bg-light px-2">

    <div class="d-flex justify-content-between align-items-center container">

      <a class="navbar-brand" href="/">Əsas səhifə</a>

 

      <div>

        <% if (!currentUser) { %>

        <!-- İstifadəçi daxil olmayıbsa -->

        <button class="btn bg-secondary">

          <a class="text-decoration-none text-white" href="/user/login"

            >Login</a

          >

        </button>

 

        <button class="btn bg-light border">

          <a class="text-decoration-none text-black" href="/user/sign-up"

            >Sign Up</a

          >

        </button>

        <% } else { %>

        <!-- İstifadəçi daxil olduqda -->

        <button class="btn bg-secondary">

          <a class="text-decoration-none text-white" href="/user/profile"

            >Profil</a

          >

        </button>

        <% } %>

      </div>

    </div>

  </nav>

</header>

 

 

Hal-hazırda uğurlu şəkildə qeydiyyatdan keçə və sayta daxil ola bilərik. Ancaq istifadəçi məlumatlarını və yazdığı xəbərləri görmək üçün controller/user.js-ə daxil olub `getUserProfile` funksiyasını yeniləyək:

 

controller/user.js

 

javascript

// İstifadəçi profilinin səhifəsini göstərir

export const getUserProfile = async (req, res) => {

  try {

    // Əgər istifadəçi sayta daxil olubsa

    if (req.isAuthenticated()) {

      const userId = req.user._id;

      // İstifadəçi məlumatları

      const currentUser = await userModel.findById(userId);

      // İstifadəçinin yazdığı xəbərlər

      const news = await newsModel.find({ user: userId });

      // Profil səhifəsinin renderi

      res.render("profile", { currentUser, news });

    } else {

      // Daxil olmadığı halda istifadəçini uyğun səhifəyə yönləndirilir

      res.redirect("/user/login");

    }

  } catch (err) {

    // Xəta baş verdikdə error səhifəsinə yönləndirilir

    console.error(err);

    res.status(500).render("error", { errorMessage: err.message });

  }

};

 

 

Hal-hazırda istifadəçi öz profilinə daxil olduqda profil məlumatlarına, əvvəlcədən yazmış olduğu xəbərlərə baxa bilər. Bunun üçün `profile.ejs` faylındakı bəzi kodları yeniləmək lazımdır:

 

profile.ejs

 

html

...

<div class="col-12 col-sm-6 col-md-8">

  <h2>İstifadəçi Profili</h2>

  <!-- İstifadəçi profili məlumatları buraya gelecek -->

 

  <% if (currentUser) { %>

  <p>Ad: <%= currentUser.username %></p>

  <p>Email: <%= currentUser.email %></p>

 

  <a href="/user/logout" class="btn btn-danger mt-3">Çıxış</a>

 

  <% } else { %>

  <!-- İstifadəçi giriş etməyibsə -->

  <p>Zəhmət olmasa giriş edin</p>

  <% } %>

</div>

 

<div class="col-12 col-sm-6 col-md-4">

  <div

    class="d-flex justify-content-between align-items-center border-bottom pb-2"

  >

    <h2>Xəbərlərim</h2>

 

    <a href="/user/create-news" class="btn btn-success">Xəbər yaz</a>

  </div>

 

  <% if (news && news.length > 0) { %> <% news.map(({ title, description }) => {

  %>

  <div class="card my-2">

    <div class="card-body">

      <h5 class="card-title">

        <a href="/single-news/123" class="text-decoration-none text-secondary">

          <%= title %>

        </a>

      </h5>

      <p class="card-text"><%= description %></p>

    </div>

  </div>

  <% }); %> <% } else { %>

  <p class="my-2">Heç bir xəbər tapılmadı</p>

  <% } %>

</div>

 

Öncəki bölümdə hazırladığımız `create-news.ejs` faylına daxil olduqda, istifadəçi yeni xəbər yaza və paylaşa biləcəkdi. İndi gəlin həmin funksionallığı əlavə edək. Bu hissədə diqqət olunmalı məqam, istifadəçi saytımıza daxil olmayıbsa, başqa sözlə `session` yoxdursa həmin səhifəyə keçid edə bilməz. Bunun əvəzinə login səhifəsinə daxil olub əvvəlcə profilinə daxil olmalıdır. İlk öncə istifadəçinin xəbər yaratmaq üçün daxil olacağı form səhifəsinə yönlənməsi üçün aşağıdakı kodu əlavə edək: 

 

controllers/user.js

 

javascript

export const getCreateNewsForm = (req, res) => {

  if (req.isAuthenticated()) {

    res.render("create-news");

  } else {

    res.redirect("/user/login");

  }

};

 

 

Yazdığımız funksiya təbiiki `routes/user.js` faylında istifadə olunmalıdır. Son funksionallığı əlavə etməzdən əvvəl verilənlər bazasında məlumatların doğru şəkildə saxlanılmasını təmin etmək üçün modellərimizdə bəzi dəyişiklikləri etmək vacibdir. Aydındır ki, bir xəbər hər hansı bir istifadəçi tərəfindən yazıla bilər. Ona görə də `News` modeli ilə istifadəçi modeli arasında əlaqəni yaratmaq və istifadəçi modelinin bir referansını xəbər modelində saxlamaq lazımdır. Bundan əlavə xəbərin şərhlərini, kimlərin bu xəbəri bəyəndiyini bilmək məqsədilə uğyun dəyərləri daxil etməliyik. Vaxt itirmədən xəbər modelini yeniləyək:

 

models/news.js

 

javascript

// Xəbər Modeli

import { Schema, model } from "mongoose";

 

const newsSchema = new Schema({

  title: { type: String, required: true },

  description: { type: String, required: true },

  date: { type: Date, default: Date.now },

  // İstifadəçi referansı (xəbəri kimin yaratdığını göstərmək üçün)

  user: { type: Schema.Types.ObjectId, ref: "User", required: true },

  // Xəbərlərin şərhləri

  comments: [

    {

      user: { type: Schema.Types.ObjectId, ref: "User", required: true },

      content: { type: String, required: true },

      date: { type: Date, default: Date.now },

    },

  ],

  // Xəbəri bəyənən istifadəçilər

  likes: [{ type: Schema.Types.ObjectId, ref: "User" }],

});

 

export const newsModel = model("News", newsSchema);

 

Bu modeldə istifadə olunan referanslar bunlardır:

 

user: Xəbəri yaradan istifadəçinin \_id-sini saxlayır.

comments: Xəbərə edilən şərhləri bildirən massiv. Hər şərhdə istifadəçi \_id, məzmun və tarix məlumatı var.

likes: Xəbəri bəyənən istifadəçilərin identifikatorunu(\_id) saxlayır.

 

Xəbər modelini yenilədikdən sonra kiçik dəyişikliyimiz etmək üçün istifadəçi modelinə daxil oluruq:

 

models/user.js

 

javascript

import { Schema, model } from "mongoose";

import passportLocalMongoose from "passport-local-mongoose";

 

const userSchema = new Schema({

  username: String,

  email: String,

  password: String,

  // İstifadəçini izləyən digər istifadəçilər

  followers: [{ type: Schema.Types.ObjectId, ref: "User" }],

});

userSchema.plugin(passportLocalMongoose);

export const userModel = model("user", userSchema);

  

 

Modelləri yenilədikdən sonra istifadəçinin xəbər yazması və həmin məlumatların databazada saxlanılmasını təmin etmənin vaxtıdır. Bunun üçün yenidən `controllers/user.js` faylında uyğun metod əlavə olunur. Ümumiyyətlə bu faylda yazmış olduğumuz funksiyaları `routes/user.js` faylında istifadə etməyi unutmayaq:

 

controllers/user.js

 

javascript

// Yeni xəbər yaratmaq

export const createNewsHandler = async (req, res) => {

  try {

    const { title, description } = req.body;

    const userId = req.user._id; // daxil olmuş istifadəçinin identifikatoru(_id)

 

    // Xəbərin verilənlər bazasına əlavə olunması

    const newNews = new newsModel({

      title,

      description,

      user: userId,

    });

 

    await newNews.save();

 

    res.redirect("/");

  } catch (error) {

    console.error(error);

    res.status(500).render("error", { errorMessage: error });

  }

};

 

 

Son dəyişikliyi etdikdən sonra yazdığımız kodların necə işlədiyiniz test edə bilərik. Gələn bölümdə istifadəçilərin xəbərləri bəyənmə, şərh bildirmə və s. kimi funksionallıqları əlavə edəcəyik. Bu bölümü uğurla başa çatdırdınız! Növbəti bölümdə görüşənədək.

 

Məqaləni hazırladı: Şamil Vasiyev

Məqaləni təsdiqlədi: Əlinemət İsiyev