Changeset b27cde6 in Klonkt


Ignore:
Timestamp:
06/21/2026 07:31:08 PM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
0251bae
Parents:
1ee5f65
Message:

fix(posts): duplicate title/slug no longer rejected — auto-unique (title-2, -3…)

A second post with the same title gave "A post with that slug already exists".
New uniqueSlug() helper automatically appends -2/-3/… on a collision, both on
create and on save; a reserved slug gets -post. No more blocking error.

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r1ee5f65 rb27cde6  
    4848});
    4949
     50// Maakt een unieke slug binnen de site: 'titel', 'titel-2', 'titel-3', …
     51// Zo wordt een tweede post met dezelfde titel NIET geweigerd ("bestaat al"),
     52// maar krijgt 'ie automatisch een vrij achtervoegsel. exceptId = de post die
     53// we bijwerken (mag z'n eigen slug houden).
     54function uniqueSlug(siteId, base, exceptId = null) {
     55  let candidate = base;
     56  let n = 2;
     57  for (;;) {
     58    const row = exceptId
     59      ? db.prepare('SELECT id FROM posts WHERE site_id = ? AND slug = ? AND id != ?').get(siteId, candidate, exceptId)
     60      : db.prepare('SELECT id FROM posts WHERE site_id = ? AND slug = ?').get(siteId, candidate);
     61    if (!row) return candidate;
     62    candidate = `${base}-${n++}`;
     63  }
     64}
     65
    5066const router = express.Router();
    5167
     
    165181
    166182  // Generate slug from title if empty
    167   const finalSlug = (slug || title || '')
     183  let finalSlug = (slug || title || '')
    168184    .toLowerCase()
    169185    .replace(/[^a-z0-9]+/g, '-')
     
    171187
    172188  if (!finalSlug) return res.status(400).send('Title or slug required');
    173   if (RESERVED_SLUGS.has(finalSlug)) return res.status(400).send('That slug is reserved');
    174 
    175   // Uniqueness check
    176   const existing = db.prepare('SELECT id FROM posts WHERE site_id = ? AND slug = ?').get(site.id, finalSlug);
    177   if (existing) return res.status(400).send('A post with that slug already exists');
     189  if (RESERVED_SLUGS.has(finalSlug)) finalSlug = `${finalSlug}-post`;
     190
     191  // Dubbele titel/slug? Automatisch uniek maken (titel-2, titel-3, …) i.p.v. weigeren.
     192  finalSlug = uniqueSlug(site.id, finalSlug);
    178193
    179194  const validTypes = new Set(['post', 'foto', 'video', 'audio']);
     
    280295  if (newSlug && newSlug !== post.slug) {
    281296    const cleaned = newSlug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
    282     if (RESERVED_SLUGS.has(cleaned)) return res.status(400).send('That slug is reserved');
    283     const conflict = db.prepare('SELECT id FROM posts WHERE site_id = ? AND slug = ? AND id != ?').get(site.id, cleaned, post.id);
    284     if (conflict) return res.status(400).send('Slug already taken');
    285     finalSlug = cleaned;
     297    const safe = RESERVED_SLUGS.has(cleaned) ? `${cleaned}-post` : cleaned;
     298    // Dubbele slug? Automatisch uniek maken i.p.v. weigeren (eigen post mag z'n slug houden).
     299    finalSlug = uniqueSlug(site.id, safe, post.id);
    286300  }
    287301
Note: See TracChangeset for help on using the changeset viewer.