262 lines
7.5 KiB
JavaScript
262 lines
7.5 KiB
JavaScript
import 'dotenv/config'
|
|
import express from 'express'
|
|
import cors from 'cors'
|
|
import multer from 'multer'
|
|
import { existsSync, mkdirSync, unlinkSync } from 'node:fs'
|
|
import { basename, dirname, extname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { randomUUID } from 'node:crypto'
|
|
import { authMiddleware, loginHandler, meHandler } from './auth.js'
|
|
import { readDb, updateDb } from './db.js'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const UPLOADS_DIR = join(__dirname, '..', 'uploads')
|
|
const PORT = Number(process.env.PORT) || 4000
|
|
|
|
if (!existsSync(UPLOADS_DIR)) mkdirSync(UPLOADS_DIR, { recursive: true })
|
|
|
|
const storage = multer.diskStorage({
|
|
destination: (_req, _file, cb) => cb(null, UPLOADS_DIR),
|
|
filename: (_req, file, cb) => {
|
|
const ext = extname(file.originalname)
|
|
cb(null, `${randomUUID()}${ext}`)
|
|
},
|
|
})
|
|
const upload = multer({ storage })
|
|
|
|
const app = express()
|
|
const corsOrigin = process.env.CORS_ORIGIN?.split(',') || ['http://localhost:5173']
|
|
|
|
app.use(cors({ origin: corsOrigin, credentials: true }))
|
|
app.use(express.json({ limit: '10mb' }))
|
|
app.use('/uploads', express.static(UPLOADS_DIR))
|
|
|
|
app.post('/api/auth/login', loginHandler)
|
|
app.get('/api/auth/me', authMiddleware, meHandler)
|
|
|
|
app.get('/api/site', (_req, res) => {
|
|
res.json(readDb().site)
|
|
})
|
|
|
|
app.put('/api/site', authMiddleware, (req, res) => {
|
|
const site = updateDb((db) => {
|
|
db.site = req.body
|
|
}).site
|
|
res.json(site)
|
|
})
|
|
|
|
app.get('/api/navigation', (_req, res) => {
|
|
res.json(readDb().navigation)
|
|
})
|
|
|
|
app.put('/api/navigation', authMiddleware, (req, res) => {
|
|
const navigation = updateDb((db) => {
|
|
db.navigation = req.body
|
|
}).navigation
|
|
res.json(navigation)
|
|
})
|
|
|
|
app.get('/api/portfolio/categories', (_req, res) => {
|
|
res.json(readDb().portfolioCategories)
|
|
})
|
|
|
|
app.get('/api/portfolio/projects', (_req, res) => {
|
|
res.json(readDb().projects)
|
|
})
|
|
|
|
app.get('/api/portfolio/projects/:id', (req, res) => {
|
|
const project = readDb().projects.find((p) => p.id === req.params.id)
|
|
if (!project) return res.status(404).json({ error: 'پروژه یافت نشد' })
|
|
res.json(project)
|
|
})
|
|
|
|
app.post('/api/portfolio/projects', authMiddleware, (req, res) => {
|
|
const project = { id: randomUUID(), ...req.body }
|
|
updateDb((db) => {
|
|
db.projects.push(project)
|
|
})
|
|
res.status(201).json(project)
|
|
})
|
|
|
|
app.put('/api/portfolio/projects/:id', authMiddleware, (req, res) => {
|
|
let updated = null
|
|
updateDb((db) => {
|
|
const i = db.projects.findIndex((p) => p.id === req.params.id)
|
|
if (i === -1) return
|
|
db.projects[i] = { ...db.projects[i], ...req.body, id: req.params.id }
|
|
updated = db.projects[i]
|
|
})
|
|
if (!updated) return res.status(404).json({ error: 'پروژه یافت نشد' })
|
|
res.json(updated)
|
|
})
|
|
|
|
app.delete('/api/portfolio/projects/:id', authMiddleware, (req, res) => {
|
|
let found = false
|
|
updateDb((db) => {
|
|
const i = db.projects.findIndex((p) => p.id === req.params.id)
|
|
if (i !== -1) {
|
|
db.projects.splice(i, 1)
|
|
found = true
|
|
}
|
|
})
|
|
if (!found) return res.status(404).json({ error: 'پروژه یافت نشد' })
|
|
res.status(204).end()
|
|
})
|
|
|
|
app.get('/api/services', (_req, res) => {
|
|
res.json(readDb().services)
|
|
})
|
|
|
|
app.put('/api/services', authMiddleware, (req, res) => {
|
|
const services = updateDb((db) => {
|
|
db.services = req.body
|
|
}).services
|
|
res.json(services)
|
|
})
|
|
|
|
app.get('/api/services/:id', (req, res) => {
|
|
const service = readDb().services.find((s) => s.id === req.params.id)
|
|
if (!service) return res.status(404).json({ error: 'خدمت یافت نشد' })
|
|
res.json(service)
|
|
})
|
|
|
|
app.put('/api/services/:id', authMiddleware, (req, res) => {
|
|
let updated = null
|
|
updateDb((db) => {
|
|
const i = db.services.findIndex((s) => s.id === req.params.id)
|
|
if (i === -1) return
|
|
db.services[i] = { ...db.services[i], ...req.body, id: req.params.id }
|
|
updated = db.services[i]
|
|
})
|
|
if (!updated) return res.status(404).json({ error: 'خدمت یافت نشد' })
|
|
res.json(updated)
|
|
})
|
|
|
|
app.get('/api/clients', (_req, res) => {
|
|
res.json(readDb().clients)
|
|
})
|
|
|
|
app.put('/api/clients', authMiddleware, (req, res) => {
|
|
const clients = updateDb((db) => {
|
|
db.clients = req.body
|
|
}).clients
|
|
res.json(clients)
|
|
})
|
|
|
|
app.get('/api/stats', (_req, res) => {
|
|
res.json(readDb().stats || [])
|
|
})
|
|
|
|
app.get('/api/blog/posts', (_req, res) => {
|
|
res.json(readDb().blogPosts.filter((p) => p.published))
|
|
})
|
|
|
|
app.get('/api/blog/posts/all', authMiddleware, (_req, res) => {
|
|
res.json(readDb().blogPosts)
|
|
})
|
|
|
|
app.get('/api/blog/posts/slug/:slug', (req, res) => {
|
|
const post = readDb().blogPosts.find(
|
|
(p) => p.slug === req.params.slug && p.published
|
|
)
|
|
if (!post) return res.status(404).json({ error: 'مطلب یافت نشد' })
|
|
res.json(post)
|
|
})
|
|
|
|
app.get('/api/blog/posts/:id', (req, res) => {
|
|
const post = readDb().blogPosts.find((p) => p.id === req.params.id)
|
|
if (!post) return res.status(404).json({ error: 'مطلب یافت نشد' })
|
|
res.json(post)
|
|
})
|
|
|
|
app.post('/api/blog/posts', authMiddleware, (req, res) => {
|
|
const post = {
|
|
id: randomUUID(),
|
|
published: false,
|
|
publishedAt: new Date().toISOString(),
|
|
...req.body,
|
|
}
|
|
updateDb((db) => {
|
|
db.blogPosts.push(post)
|
|
})
|
|
res.status(201).json(post)
|
|
})
|
|
|
|
app.put('/api/blog/posts/:id', authMiddleware, (req, res) => {
|
|
let updated = null
|
|
updateDb((db) => {
|
|
const i = db.blogPosts.findIndex((p) => p.id === req.params.id)
|
|
if (i === -1) return
|
|
db.blogPosts[i] = { ...db.blogPosts[i], ...req.body, id: req.params.id }
|
|
updated = db.blogPosts[i]
|
|
})
|
|
if (!updated) return res.status(404).json({ error: 'مطلب یافت نشد' })
|
|
res.json(updated)
|
|
})
|
|
|
|
app.delete('/api/blog/posts/:id', authMiddleware, (req, res) => {
|
|
let found = false
|
|
updateDb((db) => {
|
|
const i = db.blogPosts.findIndex((p) => p.id === req.params.id)
|
|
if (i !== -1) {
|
|
db.blogPosts.splice(i, 1)
|
|
found = true
|
|
}
|
|
})
|
|
if (!found) return res.status(404).json({ error: 'مطلب یافت نشد' })
|
|
res.status(204).end()
|
|
})
|
|
|
|
app.get('/api/media', authMiddleware, (_req, res) => {
|
|
res.json(readDb().media)
|
|
})
|
|
|
|
app.post('/api/media/upload', authMiddleware, upload.single('file'), (req, res) => {
|
|
if (!req.file) return res.status(400).json({ error: 'فایلی ارسال نشده' })
|
|
const item = {
|
|
id: randomUUID(),
|
|
filename: req.file.originalname,
|
|
url: `/uploads/${req.file.filename}`,
|
|
mimeType: req.file.mimetype,
|
|
size: req.file.size,
|
|
createdAt: new Date().toISOString(),
|
|
}
|
|
updateDb((db) => {
|
|
db.media.unshift(item)
|
|
})
|
|
res.status(201).json(item)
|
|
})
|
|
|
|
app.put('/api/media/:id', authMiddleware, (req, res) => {
|
|
const { filename } = req.body || {}
|
|
if (!filename?.trim()) return res.status(400).json({ error: 'نام فایل الزامی است' })
|
|
|
|
let updated = null
|
|
updateDb((db) => {
|
|
const i = db.media.findIndex((m) => m.id === req.params.id)
|
|
if (i === -1) return
|
|
db.media[i] = { ...db.media[i], filename: filename.trim() }
|
|
updated = db.media[i]
|
|
})
|
|
if (!updated) return res.status(404).json({ error: 'فایل یافت نشد' })
|
|
res.json(updated)
|
|
})
|
|
|
|
app.delete('/api/media/:id', authMiddleware, (req, res) => {
|
|
let removed = null
|
|
updateDb((db) => {
|
|
const i = db.media.findIndex((m) => m.id === req.params.id)
|
|
if (i === -1) return
|
|
removed = db.media.splice(i, 1)[0]
|
|
})
|
|
if (!removed) return res.status(404).json({ error: 'فایل یافت نشد' })
|
|
|
|
const filePath = join(UPLOADS_DIR, basename(removed.url))
|
|
if (existsSync(filePath)) unlinkSync(filePath)
|
|
res.status(204).end()
|
|
})
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`Noro CMS API running at http://localhost:${PORT}`)
|
|
})
|