Changeset 834bcc3 in Klonkt for src/routes/posts.js
- Timestamp:
- 06/23/2026 06:14:27 PM (3 months ago)
- Branches:
- main
- Children:
- d774679
- Parents:
- bb42dfb
- File:
-
- 1 edited
-
src/routes/posts.js (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/posts.js
rbb42dfb r834bcc3 49 49 }); 50 50 51 // Maakt een unieke slug binnen de site: 'titel', 'titel-2', 'titel-3', …52 // Zo wordt een tweede post met dezelfde titel NIET geweigerd ("bestaat al"),53 // maar krijgt 'ie automatisch een vrij achtervoegsel. exceptId = de post die54 // we bijwerken (mag z'n eigen slug houden).51 // Generates a unique slug within the site: 'title', 'title-2', 'title-3', … 52 // A second post with the same title is NOT rejected ("already exists"), 53 // but automatically gets a free suffix. exceptId = the post being updated 54 // (allowed to keep its own slug). 55 55 function uniqueSlug(siteId, base, exceptId = null) { 56 56 let candidate = base; … … 190 190 if (RESERVED_SLUGS.has(finalSlug)) finalSlug = `${finalSlug}-post`; 191 191 192 // Du bbele titel/slug? Automatisch uniek maken (titel-2, titel-3, …) i.p.v. weigeren.192 // Duplicate title/slug? Make it unique automatically (title-2, title-3, …) instead of rejecting. 193 193 finalSlug = uniqueSlug(site.id, finalSlug); 194 194 … … 199 199 let finalStatus = status || 'draft'; 200 200 let publishedAt = finalStatus === 'published' ? now : null; 201 // Release -planning: gepubliceerd + een toekomstige publish_at -> 'scheduled'202 // ( de Scheduler zet 'm live op het moment zelf). Verleden/leeg -> meteen live.201 // Release planning: published + a future publish_at -> 'scheduled' 202 // (the Scheduler makes it live at that moment). Past/empty -> live immediately. 203 203 let publishAt = null; 204 204 const pa = Date.parse(req.body.publish_at || ''); … … 297 297 const cleaned = newSlug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); 298 298 const safe = RESERVED_SLUGS.has(cleaned) ? `${cleaned}-post` : cleaned; 299 // Du bbele slug? Automatisch uniek maken i.p.v. weigeren (eigen post mag z'n slug houden).299 // Duplicate slug? Make it unique automatically instead of rejecting (own post may keep its slug). 300 300 finalSlug = uniqueSlug(site.id, safe, post.id); 301 301 } … … 310 310 } 311 311 312 // Release -planning: gepubliceerd + toekomstige publish_at -> 'scheduled'.312 // Release planning: published + future publish_at -> 'scheduled'. 313 313 let publishAt = null; 314 314 const pa = Date.parse(req.body.publish_at || ''); … … 412 412 }); 413 413 414 // Pa d naar de like-knop-partial (voor de htmx-toggle re-render).414 // Path to the like button partial (for the htmx toggle re-render). 415 415 const LIKE_PARTIAL = path.join(__dirname, '..', 'views', 'partials', 'like-button.ejs'); 416 416 417 // ==================== LIKE / FAVORIET ==================== 418 // Een ingelogde gebruiker (geen kijker — de globale guard blokkeert non-GET voor 419 // kijkers) togglet een like op een gepubliceerde post. Geeft de her-gerenderde 420 // knop terug (htmx outerHTML-swap). 417 // ==================== LIKE / FAVOURITE ==================== 418 // A logged-in user (not a viewer — the global guard blocks non-GET for viewers) 419 // toggles a like on a published post. Returns the re-rendered button (htmx outerHTML swap). 421 420 router.post('/posts/:id/like', requireAuth, (req, res) => { 422 421 const userId = req.session.user.id; … … 430 429 } else { 431 430 db.prepare('INSERT OR IGNORE INTO post_likes (post_id, user_id) VALUES (?, ?)').run(post.id, userId); 432 // Melding voor de post-auteur (notify slaat jezelf-liken over).431 // Notification for the post author (notify skips self-likes). 433 432 notify({ 434 433 userId: post.author_id, actorId: userId, actorName: req.session.user.username, type: 'like', … … 444 443 }); 445 444 446 // Favo rieten = de posts die de ingelogde gebruiker likete. Solo: binnen de447 // huidige site. Hub: over alle sites (met juiste /user/<slug>-links).445 // Favourites = posts the logged-in user has liked. Solo: within the current 446 // site. Hub: across all sites (with correct /user/<slug> links). 448 447 router.get('/favorieten', requireAuth, (req, res) => { 449 448 const userId = req.session.user.id; … … 469 468 }); 470 469 471 // Newer/Older -buren over ALLE posts in feed-volgorde. Gedeeld door de volledige472 // post -render én de fan-gate (premium fan_only), zodat de navigatie overal gelijk473 // is. Solo: binnen de site (pinned eerst, dan datum). Hub: globaal op datum.470 // Newer/Older neighbours across ALL posts in feed order. Shared by the full 471 // post render and the fan gate (premium fan_only) so navigation is consistent 472 // everywhere. Solo: within the site (pinned first, then date). Hub: globally by date. 474 473 function postNeighbors(site, post, isHub) { 475 474 const urlBaseFor = (p) => (isHub && p && p.site_slug) ? `/user/${p.site_slug}` : ''; … … 507 506 `).get(site.id, req.params.slug); 508 507 509 if (!post) return next(); // onbekende slug -> nette404 catch-all508 if (!post) return next(); // unknown slug -> clean 404 catch-all 510 509 511 510 // Permission to view: published OR (logged in + can edit) … … 515 514 } 516 515 517 // Fan-only preview (premium #3): volledige inhoud alleen voor ingelogdefans.518 // Anon ieme bezoekers krijgen een nette login-gate i.p.v. de inhoud (de titel/519 // teaser ma g elders wel als lokkertje verschijnen).516 // Fan-only preview (premium #3): full content only for logged-in fans. 517 // Anonymous visitors get a clean login gate instead of the content (the title/ 518 // teaser may still appear elsewhere as a teaser). 520 519 if (post.fan_only && !(req.session && req.session.user)) { 521 // Zelfde Newer/Older-navigatie als op een gewone post, zodat de bezoeker op522 // de fan-gate niet vastloopt maar verder kan bladeren.520 // Same Newer/Older navigation as on a normal post, so the visitor doesn't get 521 // stuck on the fan gate but can keep browsing. 523 522 const { newerPost, olderPost } = postNeighbors(site, post, res.locals.tenancy === 'hub'); 524 523 return renderPage(req, res, 'pages/fan-gate', { … … 532 531 } 533 532 534 // Statisti eken: tel de weergave (skipt beheerders + niet-gepubliceerd-eigen-preview).533 // Statistics: count the view (skips admins + unpublished own-preview). 535 534 if (post.status === 'published') recordPostView(post, req); 536 535 … … 588 587 const byAlbum = new Map(); 589 588 for (const r of albumRows) { 590 // Link-only tracks ( geen bestand) blijven in het album-overzicht(url '').589 // Link-only tracks (no file) remain in the album overview (url ''). 591 590 if (!byAlbum.has(r.album)) byAlbum.set(r.album, []); 592 591 byAlbum.get(r.album).push({ … … 625 624 } 626 625 } else { 627 // LITE -modus (KLONKT_AUDIO=off): geen eigen audio (geen ffmpeg/stream-route).628 // Extern e embeds (YouTube/SoundCloud/Spotify) blijven wel; de eigen-audio-629 // shortcodes ([[track]]/[[album]]/[[playlist]]) verwijderen we netjes.626 // LITE mode (KLONKT_AUDIO=off): no own audio (no ffmpeg/stream route). 627 // External embeds (YouTube/SoundCloud/Spotify) remain; the own-audio 628 // shortcodes ([[track]]/[[album]]/[[playlist]]) are cleanly stripped. 630 629 html = AudioEmbedService.autoembed(html); 631 630 html = AudioEmbedService.embedMediaShortcodes(html); … … 665 664 // Prev / next chronological (kept for back-compat — "post-nav" feature 666 665 // below the article still uses these as a simple linear navigation). 667 // Hub -modus: Gerelateerde posts + Newer/Older trekken uit ALLE users (alle668 // sites), nieuwste->oudste. Solo-modus: binnen de huidige site (oud gedrag).666 // Hub mode: Related posts + Newer/Older pull from ALL users (all sites), 667 // newest first. Solo mode: within the current site (old behaviour). 669 668 const isHub = res.locals.tenancy === 'hub'; 670 // Per-post URL -basis: in hub wijst een link naar/user/<site-slug>/<post-slug>.669 // Per-post URL base: in hub a link points to /user/<site-slug>/<post-slug>. 671 670 const urlBaseFor = (p) => (isHub && p && p.site_slug) ? `/user/${p.site_slug}` : ''; 672 671 673 // Newer/Older over ALLE posts (gedeelde helper — ook door de fan-gate gebruikt).672 // Newer/Older across ALL posts (shared helper — also used by the fan gate). 674 673 const { newerPost, olderPost } = postNeighbors(site, post, isHub); 675 674 … … 727 726 relatedPosts = relatedPosts.map(({ _overlap, tags, ...rest }) => ({ ...rest, _urlBase: urlBaseFor(rest) })); 728 727 729 // Likes / favo rieten: aantal + of de ingelogde gebruiker deze post likete.728 // Likes / favourites: count + whether the logged-in user liked this post. 730 729 const likeCount = db.prepare('SELECT COUNT(*) AS c FROM post_likes WHERE post_id = ?').get(post.id).c; 731 730 const likedByMe = !!(req.session?.user &&
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)