feat: lists
This commit is contained in:
177
server.js
177
server.js
@@ -64,6 +64,7 @@ function findChromeExecutable() {
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const DATA_FILE = path.join(__dirname, 'data', 'links.json');
|
||||
const LISTS_FILE = path.join(__dirname, 'data', 'lists.json');
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
@@ -83,6 +84,11 @@ async function ensureDataDir() {
|
||||
} catch {
|
||||
await fs.writeFile(DATA_FILE, JSON.stringify([]));
|
||||
}
|
||||
try {
|
||||
await fs.access(LISTS_FILE);
|
||||
} catch {
|
||||
await fs.writeFile(LISTS_FILE, JSON.stringify([]));
|
||||
}
|
||||
}
|
||||
|
||||
// Read links from file
|
||||
@@ -100,6 +106,21 @@ async function writeLinks(links) {
|
||||
await fs.writeFile(DATA_FILE, JSON.stringify(links, null, 2));
|
||||
}
|
||||
|
||||
// Read lists from file
|
||||
async function readLists() {
|
||||
try {
|
||||
const data = await fs.readFile(LISTS_FILE, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Write lists to file
|
||||
async function writeLists(lists) {
|
||||
await fs.writeFile(LISTS_FILE, JSON.stringify(lists, null, 2));
|
||||
}
|
||||
|
||||
// Extract metadata using Puppeteer (for JavaScript-heavy sites)
|
||||
async function extractMetadataWithPuppeteer(url) {
|
||||
const pptr = await getPuppeteer();
|
||||
@@ -686,6 +707,162 @@ app.delete('/api/links/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Update link's lists
|
||||
app.patch('/api/links/:id/lists', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { listIds } = req.body;
|
||||
|
||||
if (!Array.isArray(listIds)) {
|
||||
return res.status(400).json({ error: 'listIds must be an array' });
|
||||
}
|
||||
|
||||
const links = await readLinks();
|
||||
const linkIndex = links.findIndex(link => link.id === id);
|
||||
|
||||
if (linkIndex === -1) {
|
||||
return res.status(404).json({ error: 'Link not found' });
|
||||
}
|
||||
|
||||
links[linkIndex].listIds = listIds;
|
||||
await writeLinks(links);
|
||||
|
||||
res.json(links[linkIndex]);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to update link lists' });
|
||||
}
|
||||
});
|
||||
|
||||
// Lists API Routes
|
||||
|
||||
// Get all lists
|
||||
app.get('/api/lists', async (req, res) => {
|
||||
try {
|
||||
const lists = await readLists();
|
||||
res.json(lists);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to read lists' });
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new list
|
||||
app.post('/api/lists', async (req, res) => {
|
||||
try {
|
||||
const { name } = req.body;
|
||||
|
||||
if (!name || typeof name !== 'string' || name.trim().length === 0) {
|
||||
return res.status(400).json({ error: 'List name is required' });
|
||||
}
|
||||
|
||||
const lists = await readLists();
|
||||
|
||||
// Check if list with same name already exists
|
||||
const existingList = lists.find(list => list.name.toLowerCase() === name.trim().toLowerCase());
|
||||
if (existingList) {
|
||||
return res.status(409).json({ error: 'List with this name already exists' });
|
||||
}
|
||||
|
||||
const newList = {
|
||||
id: Date.now().toString(),
|
||||
name: name.trim(),
|
||||
createdAt: new Date().toISOString(),
|
||||
public: false
|
||||
};
|
||||
|
||||
lists.push(newList);
|
||||
await writeLists(lists);
|
||||
|
||||
res.status(201).json(newList);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to create list' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update a list
|
||||
app.put('/api/lists/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { name } = req.body;
|
||||
|
||||
if (!name || typeof name !== 'string' || name.trim().length === 0) {
|
||||
return res.status(400).json({ error: 'List name is required' });
|
||||
}
|
||||
|
||||
const lists = await readLists();
|
||||
const listIndex = lists.findIndex(list => list.id === id);
|
||||
|
||||
if (listIndex === -1) {
|
||||
return res.status(404).json({ error: 'List not found' });
|
||||
}
|
||||
|
||||
// Check if another list with same name exists
|
||||
const existingList = lists.find(list => list.id !== id && list.name.toLowerCase() === name.trim().toLowerCase());
|
||||
if (existingList) {
|
||||
return res.status(409).json({ error: 'List with this name already exists' });
|
||||
}
|
||||
|
||||
lists[listIndex].name = name.trim();
|
||||
await writeLists(lists);
|
||||
|
||||
res.json(lists[listIndex]);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to update list' });
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle list public status
|
||||
app.patch('/api/lists/:id/public', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { public: isPublic } = req.body;
|
||||
|
||||
if (typeof isPublic !== 'boolean') {
|
||||
return res.status(400).json({ error: 'public must be a boolean' });
|
||||
}
|
||||
|
||||
const lists = await readLists();
|
||||
const listIndex = lists.findIndex(list => list.id === id);
|
||||
|
||||
if (listIndex === -1) {
|
||||
return res.status(404).json({ error: 'List not found' });
|
||||
}
|
||||
|
||||
lists[listIndex].public = isPublic;
|
||||
await writeLists(lists);
|
||||
|
||||
res.json(lists[listIndex]);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to update list public status' });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete a list
|
||||
app.delete('/api/lists/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const lists = await readLists();
|
||||
const filtered = lists.filter(list => list.id !== id);
|
||||
|
||||
if (filtered.length === lists.length) {
|
||||
return res.status(404).json({ error: 'List not found' });
|
||||
}
|
||||
|
||||
// Remove this list from all links
|
||||
const links = await readLinks();
|
||||
links.forEach(link => {
|
||||
if (link.listIds && Array.isArray(link.listIds)) {
|
||||
link.listIds = link.listIds.filter(listId => listId !== id);
|
||||
}
|
||||
});
|
||||
await writeLinks(links);
|
||||
|
||||
await writeLists(filtered);
|
||||
res.json({ message: 'List deleted successfully' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to delete list' });
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to validate URL
|
||||
function isValidUrl(string) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user