steelforesight/panel/README.md

114 lines
3.9 KiB
Markdown

# 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 <http://localhost:3001>.
Login with the `ADMIN_USERNAME` / `ADMIN_PASSWORD` values from `.env`.
## API
All write endpoints require `Authorization: Bearer <token>`. 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
```