Dokumen ini memetakan folder dan file inti proyek, membedakan yang bawaan vs dibuat sendiri, serta alur komunikasi antar bagian. Selaras dengan kode aktual dan PERENCANAAN.md.
Cara menjalankan (2 terminal + DB):
| Terminal | Folder | Perintah | URL |
|---|---|---|---|
| — | database/ | Import schema.sql + seed.sql (PostgreSQL) | — |
| 1 | backend/ | npm run dev | http://localhost:5000 |
| 2 | frontend/ | npm run dev | http://localhost:5173 |
| Simbol | Arti |
|---|---|
| [B] | Bawaan — dari npm create vite, npm init, atau tool instalasi |
| [BUAT] | Dibuat sendiri — untuk kebutuhan latihan Portal Berita |
| [EDIT] | File bawaan yang perlu diedit agar project jalan |
| [OPS] | Operasional — otomatis / hasil penggunaan (upload, lock, build) |
| [DOC] | Dokumentasi / materi belajar |
node_modules dan dist)NEWS PKL/ ← folder project
│
├── backend/ [BUAT] folder API (Express + PostgreSQL)
│ ├── server.js [BUAT] pintu masuk — CORS, JSON, /uploads, mount routes
│ ├── package.json [B][EDIT] dependensi: express, pg, cors, dotenv, jwt, bcrypt, multer
│ ├── package-lock.json [B][OPS]
│ ├── .env [BUAT][EDIT] DB_*, PORT, JWT_SECRET, FRONTEND_URL (jangan di-commit)
│ ├── .env.example [BUAT][DOC] contoh env tanpa rahasia
│ │
│ ├── config/
│ │ └── db.js [BUAT] Pool koneksi pg
│ │
│ ├── routes/
│ │ ├── authRoutes.js [BUAT] /api/auth — register, login, /me
│ │ ├── beritaRoutes.js [BUAT] /api/berita — publik + CRUD author
│ │ ├── adminRoutes.js [BUAT] /api/admin — users + moderasi berita
│ │ └── infoWebRoutes.js [BUAT] /api/info-web — GET publik, PUT admin
│ │
│ ├── controllers/
│ │ ├── authController.js [BUAT] register, login, getMe, updateMe
│ │ ├── beritaController.js [BUAT] list/detail publik + CRUD author
│ │ ├── adminBeritaController.js [BUAT] list/detail/hapus/headline (tanpa edit isi)
│ │ ├── adminUserController.js [BUAT] kelola author
│ │ └── infoWebController.js [BUAT] get + update info_web
│ │
│ ├── models/
│ │ ├── userModel.js [BUAT] query tabel users
│ │ ├── beritaModel.js [BUAT] query tabel berita (+ headline transaksi)
│ │ └── infoWebModel.js [BUAT] query tabel info_web (1 baris)
│ │
│ ├── middlewares/
│ │ ├── authMiddleware.js [BUAT] verifikasi JWT + load user
│ │ ├── roleMiddleware.js [BUAT] requireRole("admin"|"author")
│ │ └── uploadMiddleware.js [BUAT] multer → uploads/berita|logo|sosmed
│ │
│ ├── utils/
│ │ ├── response.js [BUAT] success() / fail()
│ │ └── fileHelper.js [BUAT] hapus file gambar di disk
│ │
│ └── uploads/ [BUAT][OPS]
│ ├── berita/ [OPS] gambar berita
│ ├── logo/ [OPS] logo_web
│ └── sosmed/ [OPS] logo_wa, logo_ig
│
└── frontend/ [B] kerangka Vite + isi latihan
├── index.html [B][EDIT] judul halaman
├── vite.config.js [B] biasanya tidak perlu diubah
├── package.json [B][EDIT] tambah bootstrap, react-router-dom
├── package-lock.json [B][OPS]
├── eslint.config.js [B] linter (npm run lint)
├── .env / .env.example [BUAT][EDIT] VITE_API_URL=http://localhost:5000
│
├── public/
│ ├── placeholder-image.jpg [BUAT] fallback gambar
│ ├── favicon.svg [B]
│ └── icons.svg [B/OPS]
│
└── src/
├── main.jsx [B][EDIT] Bootstrap + BrowserRouter + App
├── App.jsx [B][EDIT] memanggil AppRoutes
├── routes/
│ └── index.jsx [BUAT] semua <Route> + ProtectedRoute
│
├── services/ [BUAT] layer HTTP ke backend
│ ├── api.js [BUAT] apiFetch, token, imageUrl
│ ├── authService.js [BUAT] /api/auth/*
│ ├── beritaService.js [BUAT] /api/berita/*
│ ├── adminService.js [BUAT] /api/admin/*
│ └── infoWebService.js [BUAT] /api/info-web
│
├── styles/
│ └── custom.css [BUAT] gaya proyek
│
├── components/
│ ├── layout/
│ │ ├── PublicLayout.jsx [BUAT] header + Outlet + footer
│ │ ├── HeaderNavbar.jsx [BUAT] menu publik + logo dari info_web
│ │ ├── FooterBar.jsx [BUAT] kontak dari info_web
│ │ ├── AdminLayout.jsx [BUAT] panel admin
│ │ ├── AuthorLayout.jsx [BUAT] panel author
│ │ └── DashboardSidebar.jsx [BUAT] sidebar + topbar + logout
│ ├── berita/
│ │ └── BeritaCard.jsx [BUAT] kartu satu berita
│ ├── home/
│ │ ├── KategoriGrid.jsx [BUAT]
│ │ └── CtaBanner.jsx [BUAT]
│ ├── common/
│ │ └── PageHeader.jsx [BUAT]
│ └── auth/
│ └── ProtectedRoute.jsx [BUAT] cek login + role
│
└── pages/
├── public/
│ ├── Home.jsx
│ ├── ListBerita.jsx
│ ├── DetailBerita.jsx
│ ├── Kategori.jsx
│ ├── Login.jsx
│ └── Daftar.jsx
└── dashboard/
├── author/
│ ├── Dashboard.jsx
│ ├── BeritaSaya.jsx
│ ├── FormBerita.jsx (tambah + edit)
│ └── Profil.jsx
└── admin/
├── Dashboard.jsx
├── ListUser.jsx
├── DetailUser.jsx
├── EditUser.jsx
├── ListBerita.jsx
├── DetailBerita.jsx
├── InfoWeb.jsx
└── Profil.jsx (re-export profil author)
Tidak ditulis di tree (bawaan npm / sisa Vite):
backend/node_modules/, frontend/node_modules/ — [B][OPS]frontend/dist/ — hasil build (opsional saat belajar)src/App.css, src/index.css, src/assets/react.svg, vite.svg — bawaan Vite, boleh diabaikan| Folder | Asal | Keterangan |
|---|---|---|
backend/ | [BUAT] | API dibuat manual (npm init + coding MVC) |
frontend/ | [B] kerangka + [BUAT] isi src/ | Dari create vite; hampir semua di src/ buatan latihan |
| File | Yang biasanya diubah |
|---|---|
index.html | Judul tab browser |
package.json | Tambah bootstrap, react-router-dom |
.env | VITE_API_URL=http://localhost:5000 |
src/main.jsx | Import Bootstrap + custom.css + Router |
src/App.jsx | Memanggil AppRoutes |
Tidak ada scaffold Express otomatis. package.json dari npm init lalu diisi dependencies. Semua file di config/, routes/, controllers/, models/, middlewares/, utils/ adalah [BUAT].
Browser (:5173) Postman / browser API (:5000)
│ │
│ fetch JSON + JWT (opsional) │
└───────────────────────────────────────────►│ backend/server.js
│ │
│ ├─► routes/*.js
│ ├─► middlewares/
│ ├─► controllers/
│ ├─► models/
│ └─► config/db.js → PostgreSQL
│
◄───────────────────────────────────────────┘ JSON + file /uploads/...
Frontend tidak memanggil PostgreSQL langsung. Semua data lewat REST API.
HTTP request
→ server.js
→ routes/auth|berita|admin|infoWeb.js
→ middlewares/ (JWT, role, multer) ← jika route dilindungi / upload
→ controllers/
→ models/
→ config/db.js → PostgreSQL
← JSON { success, message, data? }
Contoh: Author update berita → FormBerita → updateBerita(id, formData) → PUT /api/berita/:id → beritaRoutes → auth+author → beritaController.update → beritaModel.update.
index.html
→ main.jsx
→ App.jsx
→ routes/index.jsx (+ ProtectedRoute untuk dashboard)
→ pages/... (Home, Login, BeritaSaya, ...)
→ components/...
→ services/*Service.js
→ services/api.js (apiFetch + Bearer)
→ backend /api/...
Contoh: Home menampilkan headline → Home.jsx → getHeadline() + getBerita() + getInfoWeb() → setState → hero + BeritaCard + CtaBanner.
Login.jsx
→ authService.login() → POST /api/auth/login
→ authController.login → bcrypt + jwt.sign
→ setAuth(token, user) → localStorage
Request berikutnya:
→ apiFetch() → Authorization: Bearer <token>
→ authMiddleware + loadUserMiddleware
→ roleMiddleware("admin"|"author") (jika diperlukan)
→ controller
Frontend guard:
→ ProtectedRoute → getUser() dari localStorage
→ salah role → redirect dashboard yang sesuai
| Role | Prefix API utama | Halaman frontend |
|---|---|---|
| Publik | GET /api/berita, /info-web | /, /berita, /kategori/:slug |
| Author | /api/berita/mine, CRUD /api/berita, /auth/me | /dashboard/author/* |
| Admin | /api/admin/*, PUT /api/info-web | /dashboard/admin/* |
FormBerita / InfoWeb (FormData)
→ createBerita / updateBerita / updateInfoWeb
→ multer di uploadMiddleware.js
→ uploads/berita|logo|sosmed/<file>
→ path disimpan di DB (/uploads/...)
Tampil:
→ imageUrl(path) → http://localhost:5000/uploads/...
→ gagal load → PLACEHOLDER_IMAGE
| URL browser | File halaman | API utama |
|---|---|---|
/ | Home.jsx | headline, berita, info-web |
/berita | ListBerita.jsx | GET /api/berita |
/berita/:id | DetailBerita.jsx | GET /api/berita/:id |
/kategori/:slug | Kategori.jsx | GET /api/berita?kategori=... |
/login, /daftar | Login, Daftar | POST /auth/login|register |
/dashboard/author/* | pages/dashboard/author/* | berita milik sendiri + profil |
/dashboard/admin/* | pages/dashboard/admin/* | adminService + info-web |
| Tabel | Model | Dipakai untuk |
|---|---|---|
users | userModel.js | Auth, profil, kelola author |
berita | beritaModel.js | Portal + CRUD author + headline admin |
info_web | infoWebModel.js | Navbar, footer, form Info Web |
backend/server.js → routes/beritaRoutes.js → beritaController.js → beritaModel.jsfrontend/src/main.jsx → App.jsx → routes/index.jsx → pages/public/Home.jsxservices/api.js → beritaService.js → BeritaCard.jsxLogin.jsx + authService.js + authMiddleware.js + ProtectedRoute.jsxAuthorLayout → BeritaSaya / FormBeritaAdminLayout → ListBerita / ListUser / InfoWebpackage.json di root — backend dan frontend dijalankan terpisah (2 terminal).pg), bukan MySQL.frontend/dist/ hanya setelah npm run build; untuk latihan cukup npm run dev.backend/uploads/ hasil upload nyata; jangan mengandalkan isi folder itu di git.backend/ dan frontend/ yang dijalankan sistem.