# Andishkade Foolad — Admin Panel A standalone Node/Express + SQLite admin panel for managing articles published on the Andishkade Foolad site. ## Features - Login (single admin, bcrypt-hashed password, JWT session) - CRUD for articles with all fields used on the site: title, category, type, author / role / initial, publish date (Jalali string), pages, price, isFree, summary, body, cover image, tags, featured flag - Cover image upload (multer, stored on disk under `uploads/`) - Public read API (`GET /api/articles`) that the main React site can consume ## Stack - Node 20+ - Express 4 - `better-sqlite3` (single-file `data.db`) - `multer` for uploads - `bcryptjs` + `jsonwebtoken` for auth - Vanilla JS admin UI (no build step) --- ## First-time setup ```bash cd panel npm install cp .env.example .env # then edit .env, especially JWT_SECRET and ADMIN_PASSWORD npm run seed # creates the admin user from .env npm start ``` Then open . Login with the `ADMIN_USERNAME` / `ADMIN_PASSWORD` values from `.env`. ## API All write endpoints require `Authorization: Bearer `. The token is returned by `POST /api/auth/login`. | Method | Path | Auth | Purpose | |--------|-----------------------|------|-----------------------------------| | POST | `/api/auth/login` | — | `{username, password}` → `{token, user}` | | GET | `/api/auth/me` | yes | current user info | | GET | `/api/articles` | — | list articles (newest first) | | GET | `/api/articles/:id` | — | single article | | POST | `/api/articles` | yes | create article | | PUT | `/api/articles/:id` | yes | update article | | DELETE | `/api/articles/:id` | yes | delete article | | POST | `/api/uploads` | yes | multipart `file` → `{url}` | | GET | `/api/health` | — | `{ok: true}` | ### Article shape ```jsonc { "id": "abc123…", "title": "…", "category": "بازار جهانی", "type": "special", "author": "دکتر علی محمدی", "authorRole": "مدیر ارشد پژوهش", "authorInitial": "م", "publishDate": "بهمن ۱۴۰۳", "pages": 84, "price": 850000, "isFree": false, "summary": "…", "body": "متن کامل …", "coverImage": "/uploads/abc.jpg", "tags": ["تجارت جهانی", "صادرات"], "featured": true, "createdAt": "2026-05-26T13:20:00.000Z", "updatedAt": "2026-05-26T13:20:00.000Z" } ``` This shape is intentionally compatible with the `Report` type in [`../src/data/reports.ts`](../src/data/reports.ts), so the main site can swap the hardcoded array for `fetch('/api/articles')` without touching its UI components. ## Wiring the main site to the panel In the main Vite app, replace the hardcoded import: ```ts // src/lib/articles.ts const API = import.meta.env.VITE_PANEL_API || 'http://localhost:3001'; export async function fetchReports() { const res = await fetch(`${API}/api/articles`); return res.json(); } ``` Then in the consuming components (e.g. `LatestNewsBentoSection.tsx`), call `fetchReports()` inside a `useEffect` / React Query hook instead of the static `reports` array. Set `VITE_PANEL_API` in the main app's `.env` to the panel's public URL when deploying. ## Deployment notes - The panel listens on `PORT` (default 3001). It serves the admin UI at `/` and exposes `/api/*`. - Persist the `data.db` file and the `uploads/` folder — these are your content. Both are gitignored. - For HF Spaces, the panel is **not** the same image as the main site; deploy it as a separate Space (Docker SDK) or on a small VPS. - Always change `JWT_SECRET` and `ADMIN_PASSWORD` in production. ## Reset admin password ```bash sqlite3 data.db "DELETE FROM users WHERE username='admin';" # edit .env with the new password, then: npm run seed ```