Files
Church-Music/new-site/backend/api/songs.js

251 lines
5.5 KiB
JavaScript

import express from "express";
import { v4 as uuidv4 } from "uuid";
import { validate, songValidation } from "../middleware/validate.js";
const router = express.Router();
// In-memory store (replace with database in production)
const songs = new Map([
[
"1",
{
id: "1",
title: "Amazing Grace",
artist: "John Newton",
key: "G",
originalKey: "G",
tempo: 72,
category: "Hymn",
lyrics: `[Verse 1]
[G]Amazing [D]grace, how [G]sweet the [G7]sound
That [C]saved a [G]wretch like [Em]me
I [G]once was [D]lost, but [G]now am [Em]found
Was [G]blind but [D]now I [G]see`,
createdBy: "system",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
],
[
"2",
{
id: "2",
title: "How Great Is Our God",
artist: "Chris Tomlin",
key: "C",
originalKey: "C",
tempo: 78,
category: "Contemporary",
lyrics: `[Verse 1]
The [C]splendor of the [Am]King
[F]Clothed in majesty [C]
Let all the earth re[Am]joice
[F]All the earth re[G]joice`,
createdBy: "system",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
],
]);
// Search songs (for worship list)
router.get("/search", (req, res) => {
const { q } = req.query;
if (!q) {
return res.json({
success: true,
songs: [],
total: 0,
});
}
const searchLower = q.toLowerCase();
const result = Array.from(songs.values()).filter(
(song) =>
song.title.toLowerCase().includes(searchLower) ||
song.artist.toLowerCase().includes(searchLower),
);
res.json({
success: true,
songs: result,
total: result.length,
});
});
// Get all songs
router.get("/", (req, res) => {
const { search, key, category, sort } = req.query;
let result = Array.from(songs.values());
// Search filter
if (search) {
const searchLower = search.toLowerCase();
result = result.filter(
(song) =>
song.title.toLowerCase().includes(searchLower) ||
song.artist.toLowerCase().includes(searchLower),
);
}
// Key filter
if (key) {
result = result.filter((song) => song.key === key);
}
// Category filter
if (category) {
result = result.filter((song) => song.category === category);
}
// Sort
if (sort) {
const [field, order] = sort.split(":");
result.sort((a, b) => {
const aVal = a[field] || "";
const bVal = b[field] || "";
const comparison =
typeof aVal === "string" ? aVal.localeCompare(bVal) : aVal - bVal;
return order === "desc" ? -comparison : comparison;
});
}
res.json({
songs: result,
total: result.length,
});
});
// Get single song
router.get("/:id", (req, res) => {
const song = songs.get(req.params.id);
if (!song) {
return res.status(404).json({ error: "Song not found" });
}
res.json({ song });
});
// Create song
router.post("/", validate(songValidation), (req, res) => {
const { title, artist, key, tempo, category, lyrics } = req.body;
const song = {
id: uuidv4(),
title,
artist: artist || "Unknown",
key: key || "C",
originalKey: key || "C",
tempo: tempo || 72,
category: category || "Contemporary",
lyrics: lyrics || "",
createdBy: req.user.id,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
songs.set(song.id, song);
res.status(201).json({
message: "Song created successfully",
song,
});
});
// Update song
router.put("/:id", validate(songValidation), (req, res) => {
const song = songs.get(req.params.id);
if (!song) {
return res.status(404).json({ error: "Song not found" });
}
const { title, artist, key, tempo, category, lyrics } = req.body;
const updatedSong = {
...song,
title: title || song.title,
artist: artist || song.artist,
key: key || song.key,
tempo: tempo || song.tempo,
category: category || song.category,
lyrics: lyrics !== undefined ? lyrics : song.lyrics,
updatedAt: new Date().toISOString(),
};
songs.set(song.id, updatedSong);
res.json({
message: "Song updated successfully",
song: updatedSong,
});
});
// Delete song
router.delete("/:id", (req, res) => {
const song = songs.get(req.params.id);
if (!song) {
return res.status(404).json({ error: "Song not found" });
}
songs.delete(req.params.id);
res.json({ message: "Song deleted successfully" });
});
// Duplicate song
router.post("/:id/duplicate", (req, res) => {
const song = songs.get(req.params.id);
if (!song) {
return res.status(404).json({ error: "Song not found" });
}
const duplicateSong = {
...song,
id: uuidv4(),
title: `${song.title} (Copy)`,
createdBy: req.user.id,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
songs.set(duplicateSong.id, duplicateSong);
res.status(201).json({
message: "Song duplicated successfully",
song: duplicateSong,
});
});
// Transpose song
router.post("/:id/transpose", (req, res) => {
const song = songs.get(req.params.id);
if (!song) {
return res.status(404).json({ error: "Song not found" });
}
const { targetKey, useFlats } = req.body;
// Transposition logic would go here
// For now, just return the song with updated key
const transposedSong = {
...song,
key: targetKey || song.key,
// In a real implementation, lyrics would be transposed
};
res.json({
message: "Song transposed",
song: transposedSong,
});
});
export default router;