fromis_9/backend/routes/schedules.js
caadiq 346d6529f2 Meilisearch 검색 엔진 도입
- Docker Compose에 Meilisearch 서비스 추가
- meilisearch.js 서비스 생성 (초기화, CRUD, 검색)
- 공개 일정 API에 Meilisearch 검색 통합
- 일정 생성/삭제 시 Meilisearch 자동 동기화
- YouTube 봇 일정 추가 시 Meilisearch 동기화
- sync-search API 추가 (기존 데이터 일괄 동기화)
- 다중 키워드, 오타 허용, 유사어 검색 지원
2026-01-06 08:22:43 +09:00

127 lines
3.5 KiB
JavaScript

import express from "express";
import pool from "../lib/db.js";
import { searchSchedules } from "../services/meilisearch.js";
const router = express.Router();
// 공개 일정 목록 조회 (검색 포함)
router.get("/", async (req, res) => {
try {
const { search } = req.query;
// 검색어가 있으면 Meilisearch 사용
if (search && search.trim()) {
const results = await searchSchedules(search.trim());
return res.json(results);
}
// 검색어 없으면 DB에서 전체 조회
const [schedules] = await pool.query(`
SELECT
s.id,
s.title,
s.description,
s.date,
s.time,
s.category_id,
s.source_url,
s.source_name,
s.location_name,
c.name as category_name,
c.color as category_color,
GROUP_CONCAT(m.name ORDER BY m.id SEPARATOR ',') as member_names
FROM schedules s
LEFT JOIN schedule_categories c ON s.category_id = c.id
LEFT JOIN schedule_members sm ON s.id = sm.schedule_id
LEFT JOIN members m ON sm.member_id = m.id
GROUP BY s.id
ORDER BY s.date DESC, s.time DESC
`);
res.json(schedules);
} catch (error) {
console.error("일정 목록 조회 오류:", error);
res.status(500).json({ error: "일정 목록 조회 중 오류가 발생했습니다." });
}
});
// 카테고리 목록 조회
router.get("/categories", async (req, res) => {
try {
const [categories] = await pool.query(`
SELECT id, name, color, sort_order
FROM schedule_categories
ORDER BY sort_order ASC
`);
res.json(categories);
} catch (error) {
console.error("카테고리 조회 오류:", error);
res.status(500).json({ error: "카테고리 조회 중 오류가 발생했습니다." });
}
});
// 개별 일정 조회
router.get("/:id", async (req, res) => {
try {
const { id } = req.params;
const [schedules] = await pool.query(
`
SELECT
s.*,
c.name as category_name,
c.color as category_color
FROM schedules s
LEFT JOIN schedule_categories c ON s.category_id = c.id
WHERE s.id = ?
`,
[id]
);
if (schedules.length === 0) {
return res.status(404).json({ error: "일정을 찾을 수 없습니다." });
}
res.json(schedules[0]);
} catch (error) {
console.error("일정 조회 오류:", error);
res.status(500).json({ error: "일정 조회 중 오류가 발생했습니다." });
}
});
// Meilisearch 동기화 API
router.post("/sync-search", async (req, res) => {
try {
const { syncAllSchedules } = await import("../services/meilisearch.js");
// DB에서 모든 일정 조회
const [schedules] = await pool.query(`
SELECT
s.id,
s.title,
s.description,
s.date,
s.time,
s.category_id,
s.source_url,
s.source_name,
c.name as category_name,
c.color as category_color,
GROUP_CONCAT(m.name ORDER BY m.id SEPARATOR ' ') as member_names
FROM schedules s
LEFT JOIN schedule_categories c ON s.category_id = c.id
LEFT JOIN schedule_members sm ON s.id = sm.schedule_id
LEFT JOIN members m ON sm.member_id = m.id
GROUP BY s.id
`);
const count = await syncAllSchedules(schedules);
res.json({ success: true, synced: count });
} catch (error) {
console.error("Meilisearch 동기화 오류:", error);
res.status(500).json({ error: "동기화 중 오류가 발생했습니다." });
}
});
export default router;