← back to Jill Website

src/routes/admin.ts

187 lines

import { Router, Request, Response } from 'express';
import multer from 'multer';
import path from 'path';
import { PageContentModel } from '../models/PageContent';
import { requireAuth } from '../middleware/auth';

// Configure multer for file uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, path.join(__dirname, '../../public/uploads'));
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({
  storage: storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit
  fileFilter: (req, file, cb) => {
    const allowedTypes = /jpeg|jpg|png|gif|webp/;
    const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
    const mimetype = allowedTypes.test(file.mimetype);
    if (extname && mimetype) {
      cb(null, true);
    } else {
      cb(new Error('Only image files are allowed'));
    }
  }
});

export function createAdminRouter(pageContentModel?: PageContentModel): Router {
  const router = Router();
  const model = pageContentModel || new PageContentModel();

  // All admin routes require authentication
  router.use(requireAuth);

  // GET /admin - Admin CMS interface
  router.get('/', async (req: Request, res: Response) => {
    try {
      const pages = await model.findAll();
      res.render('admin', {
        pages,
        currentYear: new Date().getFullYear()
      });
    } catch (error) {
      console.error('Error loading admin page:', error);
      res.status(500).json({
        success: false,
        error: 'Failed to load admin page'
      });
    }
  });

  // GET /admin/api/pages - Get all pages
  router.get('/api/pages', async (req: Request, res: Response) => {
    try {
      const pages = await model.findAll();
      res.json({
        success: true,
        data: pages
      });
    } catch (error) {
      console.error('Error fetching pages:', error);
      res.status(500).json({
        success: false,
        error: 'Failed to fetch pages'
      });
    }
  });

  // GET /admin/api/pages/:id - Get single page
  router.get('/api/pages/:id', async (req: Request, res: Response) => {
    try {
      const idParam = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
      const id = parseInt(idParam, 10);
      if (isNaN(id)) {
        return res.status(400).json({
          success: false,
          error: 'Invalid page ID'
        });
      }

      const page = await model.findById(id);
      if (!page) {
        return res.status(404).json({
          success: false,
          error: 'Page not found'
        });
      }

      res.json({
        success: true,
        data: page
      });
    } catch (error) {
      console.error('Error fetching page:', error);
      res.status(500).json({
        success: false,
        error: 'Failed to fetch page'
      });
    }
  });

  // PUT /admin/api/pages/:id - Update page content
  router.put('/api/pages/:id', async (req: Request, res: Response) => {
    try {
      if (!req.body) {
        return res.status(400).json({
          success: false,
          error: 'Request body is required'
        });
      }

      const idParam = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
      const id = parseInt(idParam, 10);
      if (isNaN(id)) {
        return res.status(400).json({
          success: false,
          error: 'Invalid page ID'
        });
      }

      const { page_title, content, gallery_images } = req.body;

      // Get existing page to preserve slug
      const existingPage = await model.findById(id);
      if (!existingPage) {
        return res.status(404).json({
          success: false,
          error: 'Page not found'
        });
      }

      const updatedPage = await model.update(existingPage.page_slug, {
        page_slug: existingPage.page_slug,
        page_title: page_title || existingPage.page_title,
        content: content || existingPage.content,
        gallery_images: gallery_images || existingPage.gallery_images
      });

      res.json({
        success: true,
        data: updatedPage,
        message: 'Page updated successfully'
      });
    } catch (error) {
      console.error('Error updating page:', error);
      res.status(500).json({
        success: false,
        error: error instanceof Error ? error.message : 'Failed to update page'
      });
    }
  });

  // POST /admin/api/upload - Upload image
  router.post('/api/upload', upload.single('image'), (req: Request, res: Response) => {
    try {
      if (!req.file) {
        return res.status(400).json({
          success: false,
          error: 'No file uploaded'
        });
      }

      const imageUrl = `/uploads/${req.file.filename}`;
      res.json({
        success: true,
        data: { url: imageUrl },
        message: 'Image uploaded successfully'
      });
    } catch (error) {
      console.error('Error uploading image:', error);
      res.status(500).json({
        success: false,
        error: 'Failed to upload image'
      });
    }
  });

  return router;
}

export default createAdminRouter();