Files
admin/server/index.js
T

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}`)
})