Система ролей для блог-платформи
Уяви: ти створюєш блог-платформу, де різні люди мають різні права. Автор може писати чернетки, але не може публікувати їх сам. Модератор перевіряє та публікує контент. Адмін може все.
Така система дозволів (permissions) — основа будь-якого реального проєкту: від WordPress до GitHub. Ти побудуєш її з нуля, використовуючи класи та наслідування.
Файлова структура
blog-roles/
├── index.html ← мінімальна сторінка (підключає script.js)
└── script.js ← весь код тут
Як працюють дозволи
Вся система побудована на одному простому принципі: кожна роль має набір дозволів (permissions), а перевірка — це один метод hasPermission(action).
Ось які дозволи існують у нашій платформі:
| Дозвіл | Що дозволяє |
|---|---|
create:draft | Створити чернетку поста |
edit:own-draft | Редагувати свою чернетку |
delete:own-draft | Видалити свою чернетку |
publish:post | Опублікувати чернетку (зробити її видимою) |
unpublish:post | Зняти пост з публікації |
edit:any-post | Редагувати будь-який пост |
delete:any-post | Видалити будь-який пост |
manage:users | Керувати користувачами |
Частина 1: Базовий клас User
Кожен зареєстрований користувач — це User. Створи клас, який:
- Приймає options-об'єкт
{ name, email, age }при створенні - Валідує дані в конструкторі — кидає
Error, якщо:- Ім'я коротше за 2 символи
- Email не містить
@ - Вік не в діапазоні 16–120
- Зберігає свій набір дозволів — для звичайного User це:
create:draft,edit:own-draft,delete:own-draft - Має метод
hasPermission(action)— повертаєtrue, якщо дозвіл є в наборі - Має getter
role, який повертає"user" - Має getter
info, який повертає рядок:"Ім'я (email) [role]" - Має метод
greet(), який повертає привітання з іменем
Як це має працювати:
const user = new User({ name: "Олексій", email: "alex@blog.com", age: 25 });
console.log(user.info); // "Олексій (alex@blog.com) [user]"
console.log(user.greet()); // "Привіт, я Олексій!"
// Автор може створювати чернетки, але не може публікувати
console.log(user.hasPermission("create:draft")); // true
console.log(user.hasPermission("edit:own-draft")); // true
console.log(user.hasPermission("publish:post")); // false
console.log(user.hasPermission("manage:users")); // false
// Валідація
new User({ name: "", email: "test@mail.com", age: 25 });
// Error: ім'я занадто коротке
Частина 2: Клас Moderator
Модератор — це User з додатковими правами на публікацію контенту. Створи клас Moderator, який:
- Наслідує
User - Має всі дозволи User плюс:
publish:post,unpublish:post,edit:any-post - Перевизначає getter
role— повертає"moderator"
Зверни увагу: hasPermission не потрібно переписувати — він працює однаково для всіх ролей, просто набір дозволів більший.
Як це має працювати:
const mod = new Moderator({ name: "Марія", email: "maria@blog.com", age: 30 });
console.log(mod.info); // "Марія (maria@blog.com) [moderator]"
// Модератор може все, що й User...
console.log(mod.hasPermission("create:draft")); // true
console.log(mod.hasPermission("edit:own-draft")); // true
// ...плюс публікація та редагування чужих постів
console.log(mod.hasPermission("publish:post")); // true
console.log(mod.hasPermission("edit:any-post")); // true
// Але не може керувати користувачами
console.log(mod.hasPermission("manage:users")); // false
Частина 3: Клас Admin
Адмін може все. Створи клас Admin, який:
- Наслідує
User(абоModerator— подумай, що логічніше) hasPermission()завжди повертаєtrue— адмін має доступ до всього- Перевизначає getter
role— повертає"admin"
Як це має працювати:
const admin = new Admin({ name: "Катерина", email: "kate@blog.com", age: 28 });
console.log(admin.info); // "Катерина (kate@blog.com) [admin]"
// Адмін може все
console.log(admin.hasPermission("publish:post")); // true
console.log(admin.hasPermission("manage:users")); // true
console.log(admin.hasPermission("delete:any-post")); // true
// Навіть дозволи, яких немає в таблиці — адмін все одно може
console.log(admin.hasPermission("launch:rockets")); // true
Частина 4: Клас UserManager
Тепер потрібен менеджер, який керує всіма користувачами платформи. Створи клас UserManager, який:
- Зберігає список користувачів у приватному полі
#users addUser(user)— додати користувача. Якщо email вже існує — кинути помилкуremoveUser(email)— видалити за emailfindByEmail(email)— знайти користувачаgetByRole(role)— повернути масив користувачів з конкретною роллю ("user","moderator","admin")getAllInfo()— масив рядківinfoвсіх користувачів- getter
count— кількість користувачів whoCanPublish()— повернути масив тих, хто має дозвілpublish:post
Як це має працювати:
const manager = new UserManager();
manager.addUser(new User({ name: "Олексій", email: "alex@blog.com", age: 25 }));
manager.addUser(new Moderator({ name: "Марія", email: "maria@blog.com", age: 30 }));
manager.addUser(new Admin({ name: "Катерина", email: "kate@blog.com", age: 28 }));
console.log(manager.count); // 3
// Хто може публікувати пости?
const publishers = manager.whoCanPublish();
console.log(publishers.length); // 2 (модератор + адмін)
// Фільтр за роллю
console.log(manager.getByRole("moderator").length); // 1
// Дублікат email
manager.addUser(new User({ name: "Інший", email: "alex@blog.com", age: 20 }));
// Error: користувач з таким email вже існує
console.log(manager.getAllInfo());
// ["Олексій (alex@blog.com) [user]",
// "Марія (maria@blog.com) [moderator]",
// "Катерина (kate@blog.com) [admin]"]
Фінальна перевірка
Напиши в кінці script.js код, який створює команду блог-платформи та виводить у консоль звіт:
=== Блог-платформа: команда ===
Всього користувачів: 5
Адміни: Катерина
Модератори: Марія, Сергій
Автори: Олексій, Дмитро
Хто може публікувати: Марія, Сергій, Катерина
Хто може керувати користувачами: Катерина
Бонус
- Статичний метод
User.fromObject(obj)— створює потрібний клас (User, Moderator або Admin) залежно від поляroleв об'єкті. Корисно для завантаження даних з API - Метод
sortByAge()у UserManager — повертає копію масиву, відсортовану за віком. Оригінальний масив не змінюється - Перевірка дозволу перед дією: напиши функцію
tryAction(user, action), яка перевіряє дозвіл і повертає"Ім'я виконав action"або"Ім'я: доступ заборонено для action"
Підказки
- Дозволи зручно зберігати як масив рядків у класі. В конструкторі дочірнього класу додай свої дозволи до батьківських через spread:
[...super_permissions, "publish:post"] super(options)у конструкторі дочірнього класу передає весь об'єкт батьківському — він візьме звідти те, що йому потрібно- Для Admin не потрібен масив дозволів — просто перевизнач
hasPermission(), щоб завжди повертавtrue - Приватне поле
#users = []— до нього не можна дістатися ззовні класу includes()— найпростіший спосіб перевірити, чи є рядок у масиві
Критерії оцінки
| Критерій | Бали |
|---|---|
| Клас User: options-об'єкт, валідація, permissions, hasPermission, info, greet | 25 |
| Клас Moderator: наслідування, розширені дозволи, role | 20 |
| Клас Admin: наслідування, hasPermission завжди true, role | 15 |
| Клас UserManager: приватне поле, addUser з перевіркою, getByRole, whoCanPublish | 25 |
| Використання private fields, static, getters, наслідування | 15 |
| Бонус: fromObject / sortByAge / tryAction | +15 |