news-cms-module 0.1.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # News CMS Module
2
+
3
+ Modul Content Management System (CMS) Berita yang siap pakai untuk aplikasi Express.js. Modul ini menangani manajemen database (Sequelize), logika bisnis, hingga tampilan antarmuka (EJS) secara otomatis.
4
+
5
+ ## Fitur
6
+ - **Auto-Sync Database**: Membuat tabel `news`, `content_news`, dan `visitor_logs` secara otomatis.
7
+ - **Tracking Pengunjung**: Sistem pelacakan unik pengunjung per berita dalam 24 jam.
8
+ - **Trending News**: Menampilkan 10 berita paling populer berdasarkan jumlah pengunjung 24 jam terakhir.
9
+ - **CMS Admin**: Dashboard manajemen berita dengan prefix yang dapat diatur.
10
+ - **Static Assets**: CSS internal yang sudah terintegrasi.
11
+
12
+ ## Instalasi
13
+
14
+ Jalankan perintah berikut pada terminal proyek Anda:
15
+
16
+ ```bash
17
+ npm install news-cms-module ejs express-session mysql2
18
+ ```
19
+
20
+ ## Panduan Penggunaan (app.js)
21
+
22
+ ```javascript
23
+ const express = require('express');
24
+ require('dotenv').config();
25
+ const newsModule = require('news-cms-module');
26
+
27
+ const app = express();
28
+
29
+ const dbConfig = {
30
+ host: process.env.DB_HOST,
31
+ port: process.env.DB_PORT || 3036, // Sesuaikan dengan port db anda
32
+ username: process.env.DB_USER_NAME,
33
+ password: process.env.DB_PASSWORD,
34
+ database: process.env.DB_NAME,
35
+ dialect: 'mysql',
36
+ dialectOptions: {
37
+ ssl: {
38
+ require: true,
39
+ rejectUnauthorized: false // Set ke false jika tidak pakai file sertifikat .pem
40
+ }
41
+ },
42
+ // Opsional: tambahkan ini untuk mencegah timeout di koneksi lambat
43
+ pool: {
44
+ max: 5,
45
+ min: 0,
46
+ acquire: 30000,
47
+ idle: 10000
48
+ }
49
+ };
50
+
51
+ // Konfigurasi URL Dinamis
52
+ const PORT = process.env.PORT || 3000;
53
+ const APP_URL = process.env.APP_URL || `http://localhost:${PORT}`;
54
+ const NEWS_PREFIX = '/berita'; // Anda bisa mengganti ini sesuka hati (misal: /news)
55
+
56
+ async function startServer() {
57
+ // 1. Inisialisasi Middleware Global
58
+ app.use(express.json());
59
+ app.use(express.urlencoded({ extended: true }));
60
+ app.use(express.static('public'));
61
+
62
+ try {
63
+ // 2. Inisialisasi Package (Dijalankan secara ASYNC)
64
+ const newsRouter = await newsModule(dbConfig, {
65
+ adminRoutePrefix: '/cms-admin',
66
+ newsPrefix: NEWS_PREFIX,
67
+ baseUrl: APP_URL + NEWS_PREFIX,
68
+ });
69
+
70
+ // 3. Pasang Router ke Prefix URL
71
+ // Ini akan membuat rute: /berita/list, /berita/cms-admin, dll.
72
+ app.use(NEWS_PREFIX, newsRouter);
73
+
74
+ // 4. Jalankan Server
75
+ app.listen(PORT, () => {
76
+ console.log(`Server Berhasil Dijalankan!`);
77
+ console.log(`Base URL : ${APP_URL}`);
78
+ console.log(`API Berita : ${APP_URL}${NEWS_PREFIX}/list`);
79
+ console.log(`Admin CMS : ${APP_URL}${NEWS_PREFIX}/cms-admin/dashboard`);
80
+ });
81
+ } catch (error) {
82
+ console.error("Gagal menjalankan server:", error);
83
+ }
84
+ }
85
+
86
+ startServer();
87
+ ```
88
+ pastikan untuk menyesuaikan bagian authAdminMiddleware pada module di bagian middlewares. Sesuaikan dengan preferensi tabel user masing-masing.
89
+
90
+
91
+ github news-cms-module: https://github.com/DewaAry-D/news-cms-module.git
92
+ github contoh implementasi: https://github.com/DewaAry-D/dummy-news.git
package/config/index.js CHANGED
@@ -4,6 +4,8 @@ const defaultConfig = {
4
4
  // Pengaturan Router
5
5
  adminRoutePrefix: '/admin', // Prefix default untuk route CRUD admin
6
6
  publicRoutePrefix: '/', // Prefix default untuk route publik
7
+ newsPrefix: '/berita', // Prefix defaul umum contohnya /berita/{..}
8
+ baseURL: '', // Prefix defaul untuk fetch API
7
9
 
8
10
  // Pengaturan Views
9
11
  viewEngine: 'ejs', // Template engine default
@@ -24,10 +26,8 @@ const defaultConfig = {
24
26
  * @returns {object} Objek konfigurasi akhir yang lengkap.
25
27
  */
26
28
  module.exports = (userConfig = {}) => {
27
- // Menggunakan penyebaran (spread operator) untuk menggabungkan objek.
28
- // userConfig akan menimpa (overwrite) defaultConfig jika ada properti yang sama.
29
29
  return {
30
30
  ...defaultConfig,
31
31
  ...userConfig
32
32
  };
33
- };
33
+ };
@@ -1,50 +1,145 @@
1
+ const fs = require('fs');
2
+ const News = require('../models/News');
3
+ const { Op, fn, col, where } = require('sequelize');
4
+ const path = require('path');
5
+
1
6
  class NewsController {
2
7
  constructor(newsService, statService, config) {
3
8
  this.newsService = newsService;
4
9
  this.statService = statService;
5
- this.config = config; // Digunakan untuk passing config ke view (e.g. baseUrl)
10
+ this.config = config;
6
11
  }
7
12
 
8
- // --- Route Publik ---
9
13
  async listPublic(req, res) {
14
+ const {
15
+ page = 1,
16
+ limit = 6,
17
+ title = '',
18
+ category = ''
19
+ } = req.query;
20
+
21
+ const currentPage = parseInt(page, 10);
22
+ const perPage = parseInt(limit, 10);
23
+
24
+ const offset = (currentPage - 1) * perPage;
25
+
10
26
  try {
11
- const { rows: posts, count: total } = await this.newsService.getAllPosts(req.query.page);
27
+ const { rows: posts, count: totalItems } = await this.newsService.getAllPosts({
28
+ offset,
29
+ limit: perPage,
30
+ title,
31
+ category,
32
+ status: "PUBLISHED"
33
+ });
12
34
 
13
- res.status(200).json({
14
- success: true,
15
- data: posts,
16
- total: total,
17
- page: req.query.page
18
- })
35
+ const totalPages = Math.ceil(totalItems / perPage);
36
+
37
+ const categories = await this.newsService.getUniqueCategories();
38
+ const trending = await this.newsService.getTrendingNews();
39
+
40
+ //render
41
+ res.render(path.join(__dirname, "../views/home.ejs"), {
42
+ posts,
43
+ categories,
44
+ trending,
45
+ query: { title, category },
46
+ pagination: {
47
+ totalItems,
48
+ totalPages,
49
+ currentPage,
50
+ perPage,
51
+ hasNextPage: currentPage < totalPages,
52
+ hasPrevPage: currentPage > 1,
53
+ },
54
+ });
19
55
 
20
- // res.render('list', { posts, total, baseUrl: req.baseUrl });
21
56
  } catch (error) {
57
+ //console.error('Error loading news list:', error);
22
58
  res.status(500).json({
23
- succses: false,
24
- error: 'Error loading news list.'
59
+ success: false,
60
+ error: 'Error loading news list.',
61
+ message: error.message
25
62
  });
26
63
  }
27
64
  }
28
65
 
29
66
  async getDetail(req, res) {
30
67
  try {
31
- const post = await this.newsService.getPostBySlug(req.params.slug);
32
- if (!post) {
68
+ const news = await this.newsService.getPostBySlug(req.params.slug);
69
+ if (!news) {
70
+ return res.status(404).json({
71
+ success: false,
72
+ error: 'news tidak ditemukan'
73
+ });
74
+ }
75
+
76
+ const categories = await this.newsService.getUniqueCategories();
77
+ const recommendation = await this.newsService.getRecommendationNews(news.category);
78
+ const trending = await this.newsService.getTrendingNews();
79
+
80
+ res.render(path.join(__dirname, "../views/detail.ejs"), {
81
+ news,
82
+ categories,
83
+ recommendation,
84
+ trending,
85
+ query: {}
86
+ });
87
+ } catch (error) {
88
+ console.error('Error loading news detail:', error);
89
+ res.status(500).json({
90
+ success: false,
91
+ error: 'gagal melihat news',
92
+ message: error.message
93
+ });
94
+ }
95
+ }
96
+
97
+ async getDetailForAdmin(req, res) {
98
+ try {
99
+ const news = await this.newsService.getPostBySlugForAdmin(req.params.slug);
100
+ if (!news) {
101
+ return res.status(404).json({
102
+ success: false,
103
+ error: 'news tidak ditemukan'
104
+ });
105
+ }
106
+
107
+ res.render(path.join(__dirname, "../views/admin/detailadmin.ejs"), {
108
+ news
109
+ });
110
+ } catch (error) {
111
+ console.error('Error loading news detail for admin:', error);
112
+ res.status(500).json({
113
+ success: false,
114
+ error: 'gagal melihat news',
115
+ message: error.message
116
+ });
117
+ }
118
+ }
119
+
120
+ async getEditForAdmin(req, res) {
121
+ try {
122
+ const posts = await this.newsService.getPostBySlugForAdmin(req.params.slug);
123
+ if (!posts) {
33
124
  return res.status(404).json({
34
125
  succses: false,
35
126
  error: 'news tidak ditemukan'
36
127
  });
37
128
  }
38
129
 
39
- res.status(200).json({
40
- success: true,
41
- data: post
42
- })
130
+ const appBaseUrl = config.baseUrl;
131
+ const newsPrefix = config.newsPrefix;
132
+ const adminPrefix = config.adminRoutePrefix;
133
+
134
+ const fullApiUrl = `${appBaseUrl}${adminPrefix}/create`;
135
+ const nextUrl = `${newsPrefix}${adminPrefix}/list`
43
136
 
44
- // Render detail view
45
- // res.render('detail', { post, baseUrl: req.baseUrl });
137
+ res.render(path.join(__dirname, '../views/admin/update_news.ejs'), {
138
+ data: posts,
139
+ apiBaseUrl: fullApiUrl,
140
+ nextUrl
141
+ });
46
142
  } catch (error) {
47
- // res.status(500).send('Error loading post detail.');
48
143
  res.status(500).json({
49
144
  succses: false,
50
145
  error: 'gagal melihat news'
@@ -52,29 +147,70 @@ class NewsController {
52
147
  }
53
148
  }
54
149
 
55
- // --- Route Admin (CRUD) ---
150
+ // Route Admin (CRUD)
56
151
  async adminList(req, res) {
57
- //tambahkan ARCHIVE
58
- const { rows: posts } = await this.newsService.getAllPosts(req.query.page, 10, ['DRAFT', 'PUBLISHED']);
152
+ const {
153
+ page = 1,
154
+ limit = 10,
155
+ title = '',
156
+ category = '',
157
+ status = ''
158
+ } = req.query;
159
+
160
+ const currentPage = parseInt(page, 10);
161
+ const perPage = parseInt(limit, 10);
162
+
163
+ const offset = (currentPage - 1) * perPage;
164
+
165
+ try {
166
+ const { rows: posts, count: totalItems } = await this.newsService.getAllPosts({
167
+ offset,
168
+ limit: perPage,
169
+ title,
170
+ category,
171
+ status: status.toUpperCase()
172
+ });
59
173
 
60
- res.status(200).json({
61
- success: true,
62
- data: posts
63
- })
174
+ const totalPages = Math.ceil(totalItems / perPage);
175
+ const categories = await this.newsService.getUniqueCategories();
64
176
 
65
- //render
66
- // res.render('admin/list', { posts, baseUrl: req.baseUrl });
177
+ // Render view instead of sending JSON
178
+ res.render(path.join(__dirname, "../views/admin/list.ejs"), {
179
+ posts,
180
+ categories,
181
+ query: { title, category, status },
182
+ pagination: {
183
+ totalItems,
184
+ totalPages,
185
+ currentPage,
186
+ perPage,
187
+ hasNextPage: currentPage < totalPages,
188
+ hasPrevPage: currentPage > 1
189
+ }
190
+ });
191
+
192
+ } catch (error) {
193
+ console.error('Error loading admin news list:', error);
194
+ res.status(500).json({
195
+ success: false,
196
+ error: 'Error loading news list.',
197
+ message: error.message
198
+ });
199
+ }
67
200
  }
68
201
 
69
202
  async createPost(req, res) {
70
203
  try {
71
- const { title, summary, authorId, status, contentBlocks } = req.body;
72
204
 
73
- const slug = title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
205
+ const { title, authorName, status, contentBlocks } = req.body;
206
+ const category = req.body.category.toLowerCase();
207
+ const files = req.files;
208
+ const slug = title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
74
209
 
75
210
  const newNews = await this.newsService.createPost(
76
- { title, slug, summary, authorId, status: status || 'DRAFT' },
77
- contentBlocks
211
+ { title, slug, category, authorName, status: status || 'DRAFT' },
212
+ contentBlocks,
213
+ files
78
214
  );
79
215
 
80
216
  res.status(201).json({
@@ -86,56 +222,106 @@ class NewsController {
86
222
  // res.redirect(this.config.adminRoutePrefix + '/');
87
223
  } catch (error) {
88
224
  console.error(error);
89
- // res.status(500).send('Gagal membuat berita.');
225
+
226
+ if (req.files) {
227
+ const allFiles = [
228
+ ...(req.files['thumbnailImage'] || []),
229
+ ...(req.files['contentImages'] || [])
230
+ ];
231
+
232
+ allFiles.forEach(file => {
233
+ fs.unlink(file.path, (err) => {
234
+ if (err) console.error(`Gagal menghapus file: ${file.path}`, err);
235
+ else console.log(`Berhasil menghapus sampah file: ${file.path}`);
236
+ });
237
+ });
238
+ }
239
+
90
240
  res.status(500).json({
91
241
  succses: false,
92
242
  error: 'gagal membuat news'
93
243
  });
94
244
  }
95
245
  }
96
-
246
+
97
247
  async updatePost(req, res) {
248
+ const { id } = req.params;
98
249
  try {
99
- const { id } = req.params;
100
- const { title, summary, authorId, status, contentBlocks } = req.body;
101
-
102
- if (!id || !title || !contentBlocks) {
103
- return res.status(400).json({ success: false, message: 'Missing required fields or ID.' });
104
- }
105
-
106
- const slug = title.toLowerCase().trim().replace(/ /g, '-').replace(/[^\w-]+/g, '');
107
- const isPublished = status === 'PUBLISHED';
250
+ const { title, authorName, status, contentBlocks } = req.body;
251
+ const category = req.body.category.toLowerCase();
252
+ const files = req.files;
253
+ const slug = title ? title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') : undefined;
108
254
 
109
- const updatedPost = await this.newsService.updatePost(
255
+ const result = await this.newsService.updatePost(
110
256
  id,
111
- {
112
- title,
113
- slug,
114
- summary,
115
- authorId,
116
- status: status || 'DRAFT',
117
- publishedAt: isPublished ? new Date() : null
118
- },
119
- contentBlocks
257
+ { title, slug, category, authorName, status },
258
+ contentBlocks,
259
+ files
120
260
  );
121
261
 
122
- if (!updatedPost) {
123
- return res.status(404).json({ success: false, message: 'News post not found for update.' });
262
+ if (result.filesToDelete && result.filesToDelete.length > 0) {
263
+ result.filesToDelete.forEach(filePath => {
264
+ fs.unlink(filePath, (err) => {
265
+ if (err) console.error(`Gagal hapus file lama: ${filePath}`, err);
266
+ });
267
+ });
124
268
  }
125
269
 
126
270
  res.status(200).json({
127
271
  success: true,
128
- message: 'Post updated successfully.',
129
- data: updatedPost
272
+ message: 'Berhasil update berita',
273
+ data: result.newsItem
130
274
  });
131
275
 
132
276
  } catch (error) {
133
277
  console.error(error);
134
- res.status(500).json({ success: false, message: 'Failed to update post.', error: error.message });
278
+
279
+ if (req.files) {
280
+ const uploadedFiles = [
281
+ ...(req.files['thumbnailImage'] || []),
282
+ ...(req.files['contentImages'] || [])
283
+ ];
284
+ uploadedFiles.forEach(file => {
285
+ fs.unlink(file.path, (err) => {
286
+ if (err) console.error(`Cleanup error file gagal: ${file.path}`, err);
287
+ });
288
+ });
289
+ }
290
+
291
+ res.status(500).json({
292
+ success: false,
293
+ error: error.message || 'Gagal update news'
294
+ });
135
295
  }
136
296
  }
137
297
 
298
+ async updateStatusNews(req, res) {
299
+ const { id } = req.params;
300
+ try {
301
+ const { status } = req.body;
138
302
 
303
+ const result = await this.newsService.updateStatusNews(
304
+ id,
305
+ status
306
+ );
307
+
308
+ res.status(200).json({
309
+ success: true,
310
+ message: 'Berhasil update status berita',
311
+ data: result
312
+ });
313
+
314
+ } catch (error) {
315
+ console.error(error);
316
+
317
+ res.status(500).json({
318
+ success: false,
319
+ error: error.message || 'Gagal update news'
320
+ });
321
+ }
322
+ }
323
+
324
+ //ini blum selesai
139
325
  async deletePost(req, res) {
140
326
  try {
141
327
  const { id } = req.params;
@@ -156,6 +342,26 @@ class NewsController {
156
342
  }
157
343
  }
158
344
 
345
+ async dashboardAdmin(req, res) {
346
+ try {
347
+ const currentYear = new Date().getFullYear();
348
+
349
+ const data = await this.newsService.dashboardAdmin(currentYear);
350
+
351
+ // res.status(200).json({
352
+ // success: true,
353
+ // data
354
+ // });
355
+
356
+ res.render(path.join(__dirname, '../views/admin/dashboard.ejs'), {
357
+ data: data
358
+ });
359
+ } catch (error) {
360
+ console.error(error);
361
+ res.status(500).json({ success: false, message: 'Failed to load data.', error: error.message });
362
+ }
363
+ }
364
+
159
365
  }
160
366
 
161
367
  module.exports = NewsController;
@@ -1,25 +1,32 @@
1
- // controllers/StatController.js
2
-
3
1
  class StatController {
4
- constructor(statService) {
2
+ constructor(newsService, statService) {
3
+ this.newsService = newsService;
5
4
  this.statService = statService;
6
5
  }
7
6
 
8
- // Middleware untuk pelacakan kunjungan blum selesai
9
7
  async trackVisitMiddleware(req, res, next) {
10
- const { slug } = req.params;
11
-
12
- // Ambil post ID (idealnya dilakukan oleh service sebelum dipanggil)
13
- // Untuk demo, kita asumsikan kita punya newsId dari service atau middleware sebelumnya.
14
- // **REAL-WORLD:** Anda harus mencari newsId berdasarkan slug di sini atau di service.
15
- // const newsId = 1; // Contoh: Asumsi newsId ditemukan
8
+ try {
9
+ const slug = req.params.slug;
10
+ const post = await this.newsService.getPostBySlug(slug);
11
+
12
+ if (post) {
13
+ if (!req.session.viewedPosts) {
14
+ req.session.viewedPosts = [];
15
+ }
16
16
 
17
- const sessionId = req.sessionID || req.ip; // Gunakan sesi atau IP untuk unique ID
18
-
19
- if (newsId) {
20
- await this.statService.trackVisit(newsId, sessionId);
17
+ if (!req.session.viewedPosts.includes(post.id)) {
18
+ const sessionId = req.sessionID || req.ip;
19
+ await this.statService.trackVisit(post.id, sessionId);
20
+
21
+ req.session.viewedPosts.push(post.id);
22
+ }
23
+
24
+ req.postData = post;
25
+ }
26
+
27
+ } catch (error) {
28
+ console.error("News tracking failed but request continued:", error);
21
29
  }
22
-
23
30
  next();
24
31
  }
25
32
 
package/index.js CHANGED
@@ -1,67 +1,53 @@
1
- /**
2
- * Fungsi utama package yang di-export.
3
- * @param {object} dbConfig - Konfigurasi koneksi database dari aplikasi pengguna.
4
- * @param {object} options - Opsi tambahan (misalnya, nama folder views pengguna).
5
- * @returns {express.Router} Router Express yang sudah terkonfigurasi.
6
- */
7
- // index.js (Revisi Akhir)
8
-
9
1
  const express = require('express');
10
- // Pastikan Anda telah mendefinisikan dan mengimpor fungsi-fungsi ini
2
+ const session = require('express-session');
3
+ const path = require('path');
11
4
  const initModels = require('./models');
12
5
  const initServices = require('./services');
13
6
  const setupRoutes = require('./routes');
14
- const mergeConfig = require('./config'); // Gunakan fungsi mergeConfig yang sudah kita buat
7
+ const mergeConfig = require('./config');
15
8
 
16
- /**
17
- * Fungsi utama package yang di-export.
18
- * @param {object} dbConfig - Konfigurasi koneksi database dari aplikasi pengguna.
19
- * @param {object} userOptions - Opsi tambahan (misalnya, autoMigrate) dari pengguna.
20
- * @returns {express.Router} Router Express yang sudah terkonfigurasi.
21
- */
22
- module.exports = async (dbConfig, userOptions = {}) => { // <<< KUNCI 1: Jadikan ASYNC
9
+ module.exports = async (dbConfig, userOptions = {}) => {
23
10
 
24
- // 1. Validasi dan Konfigurasi
25
11
  if (!dbConfig || !dbConfig.database) {
26
12
  throw new Error('News module requires database configuration (dbConfig).');
27
13
  }
28
14
 
29
- // Gabungkan konfigurasi database dan opsi pengguna (walaupun di sini kita hanya menggunakan dbConfig)
30
15
  const finalConfig = mergeConfig({
31
16
  ...userOptions,
32
17
  db: dbConfig
33
18
  });
34
19
 
35
- // 2. Inisialisasi Koneksi Database dan Model
36
20
  const db = initModels(finalConfig.db);
37
21
 
38
- // 3. Sinkronisasi Tabel (Pembuatan Tabel Otomatis)
39
- // Secara default, kita asumsikan sinkronisasi ON. Pengguna bisa menonaktifkannya dengan { autoMigrate: false }
40
22
  if (finalConfig.autoMigrate !== false) {
41
23
  try {
42
24
  console.log('News module: Synchronizing database tables...');
43
- // Memanggil syncTables yang didefinisikan di models/index.js
44
25
  await db.syncTables({ alter: true });
45
26
  console.log('News module: Synchronization complete.');
46
27
  } catch (error) {
47
28
  console.error('ERROR: News module failed to synchronize database tables.', error);
48
- // Anda bisa memilih untuk melempar error agar server tidak berjalan tanpa tabel
49
29
  throw error;
50
30
  }
51
31
  }
52
-
53
- // 4. Inisialisasi Services (Injeksi Dependencies)
54
- const services = initServices(db);
55
32
 
56
- // 5. Setup Router Express
33
+ const services = initServices(db);
57
34
  const router = express.Router();
58
-
59
- // Middleware dasar untuk parsing body (penting untuk CRUD)
35
+
36
+ // 2. DAFTARKAN FOLDER PUBLIC MILIK MODUL
37
+ router.use(express.static(path.join(__dirname, 'public')));
38
+
60
39
  router.use(express.json());
61
40
  router.use(express.urlencoded({ extended: true }));
41
+ router.use(session({
42
+ secret: userOptions.sessionSecret || 'news_module_default_secret',
43
+ resave: false,
44
+ saveUninitialized: true,
45
+ cookie: {
46
+ maxAge: 24 * 60 * 60 * 1000,
47
+ secure: false
48
+ }
49
+ }));
62
50
 
63
- // 6. Setup Router dan Controllers
64
- // Route diatur, Controllers menggunakan Services dan finalConfig
65
51
  setupRoutes(router, services, finalConfig);
66
52
 
67
53
  return router;
@@ -0,0 +1,22 @@
1
+ const isAdmin = (req, res, next) => {
2
+ //buat bagian admin autentikasi ini sesuai dengan tabel dan kebutuhan anda
3
+
4
+ // 1. Cek apakah user sudah login (session ada)
5
+ // if (!req.session || !req.session.user) {
6
+ // return res.redirect('/login'); // Redirect ke halaman login jika belum auth
7
+ // }
8
+
9
+ // 2. Cek apakah role user adalah ADMIN
10
+ // Diasumsikan di tabel User Anda memiliki kolom 'role'
11
+ // if (req.session.user.role !== 'ADMIN') {
12
+ // // Jika login tapi bukan admin, arahkan ke home atau beri error 403
13
+ // return res.status(403).render('error/403', {
14
+ // message: 'Akses Ditolak: Anda bukan Administrator'
15
+ // });
16
+ // }
17
+
18
+ // 3. Jika semua terpenuhi, lanjut ke controller
19
+ next();
20
+ };
21
+
22
+ module.exports = { isAdmin };