source: Klonkt/src/routes/admin-seo.js@ f50a84b

main
Last change on this file since f50a84b was f50a84b, checked in by Robin <roboburr@…>, 4 weeks ago

Jouw teksten, en de twee dingen die ze beloofden (shaer-mbz)

De titel is nu "MusicBrainz Koppeling" met jouw omschrijving eronder, in drie
talen. Maar die omschrijving belooft twee dingen die de code nog niet deed, en
een label dat liegt is erger dan een lelijk label. Dus allebei gebouwd.

"OF JE MUSICBRAINZ ID ALS JE DIE KENT". Plak je een UUID, dan wordt hij
rechtstreeks opgezocht in plaats van gezocht. Dat is geen luxe: een
zoekopdracht OP een UUID levert bij MusicBrainz niets op, dus zonder deze tak
gaf plakken juist het slechtste resultaat.

"MET TERUG-WEG VALIDATIE". Een koppeling van onze kant is een bewering --
iedereen kan een id in een veld typen. Pas als de MusicBrainz-pagina TERUGWIJST
naar dit domein is het een paar, en dan weet een lezer dat dezelfde persoon aan
allebei de kanten stond. Dezelfde gedachte als rel="me". Het paneel zegt
"wederzijds" of "nog eenzijdig", en bij dat laatste staat erbij WAT je moet doen:
het domein op je MusicBrainz-pagina zetten onder "social networking".

Wij zetten die terugwijzing niet zelf. Kan niet via hun API, en hoort ook niet.

ECHT GEMETEN, en dat gaf de test zijn vorm: robo-burr staat in MusicBrainz met
score 100 (24abe2be-c0bc-4c63-9642-d6f89ec6a00a, "AKA roboburr") en heeft
precies EEN url-relatie -- type "social network", naar sound-fabrics.com. Voor
dev.klonkt.com is de terug-weg dus false, en dat is precies de waarde van de
controle. Die vorm staat nu in een test.

Bij een storing komt er false EN een lege lijst uit: niet kunnen kijken is niet
hetzelfde als niet gevonden, en nooit stilletjes "wederzijds".

Een controle erbij die eist dat alle drie de talen dezelfde mb_-sleutels
hebben. Bij het herschrijven waren er zeven weggevallen die het scherm nog
gebruikt -- een gat in een vertaling geeft een leeg label en dat valt nooit
vanzelf op.

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

  • Property mode set to 100644
File size: 5.8 KB
Line 
1/**
2 * Admin: advanced SEO settings for the primary site.
3 *
4 * GET /admin/seo -> form with all SEO/social fields for the main site
5 * POST /admin/seo -> save (god-only)
6 *
7 * These fields are already consumed by the <head> (shell.ejs) and the JSON-LD/
8 * OpenGraph tags, but were previously not editable anywhere. The basic
9 * fields (title/bio/robots) remain in Appearance; this is the advanced layer:
10 * title template, canonical, social share image, verification metas,
11 * publisher/JSON-LD and OpenGraph locale.
12 *
13 * Operates on the PRIMARY site (solo = the only site; hub = the company site).
14 */
15
16import express from 'express';
17import db from '../config/database.js';
18import { renderPage } from '../middleware/render.js';
19import { requireGod } from '../middleware/auth.js';
20import { getPrimarySite } from '../middleware/site.js';
21import { isMbid } from '../services/ap-core.js';
22import MusicBrainz from '../services/MusicBrainzService.js';
23
24const router = express.Router();
25
26function trimOrNull(v, max) {
27 const s = (v == null ? '' : String(v)).trim();
28 return s ? s.slice(0, max) : null;
29}
30
31// ==================== FORM ====================
32router.get('/', requireGod, (req, res) => {
33 const primary = getPrimarySite();
34 if (!primary) {
35 return res.redirect('/admin/sites/new?error=' + encodeURIComponent('Maak eerst een site aan'));
36 }
37 const site = db.prepare('SELECT * FROM sites WHERE id = ?').get(primary.id);
38
39 renderPage(req, res, 'pages/admin-seo', {
40 pageTitleKey: 'admin.t_seo',
41 pageJs: 'admin-seo',
42 bodyClass: 'on-admin',
43 site,
44 success: req.query.success || null,
45 error: req.query.error || null,
46 });
47});
48
49/**
50 * "Zoek jezelf op" -- kandidaten uit MusicBrainz (shaer-mbz).
51 *
52 * De zoekopdracht draait HIER en niet in de browser: MusicBrainz staat een
53 * verzoek per seconde toe per APPLICATIE, en dat is alleen af te dwingen als
54 * alles langs een plek gaat. Bovendien eisen ze een User-Agent met contact, en
55 * die kan een browser niet zetten.
56 *
57 * Wij kiezen NIET. Ook niet als er precies een treffer is: een verkeerd geraden
58 * MBID zet jouw naam onder andermans werk.
59 */
60router.get('/api/musicbrainz', requireGod, async (req, res) => {
61 const site = getPrimarySite(req);
62 const q = String(req.query.q || (site && (site.publisher_name || site.title)) || '').trim();
63 if (!q) return res.json({ ok: true, q: '', kandidaten: [] });
64 // Wie zijn id al kent plakt het hier. Een zoekopdracht op een UUID levert bij
65 // MusicBrainz niets op, dus zonder deze tak geeft plakken juist het slechtste
66 // resultaat.
67 if (isMbid(q)) {
68 const een = await MusicBrainz.haalArtiest(q);
69 return res.json({ ok: true, q, kandidaten: een ? [een] : [] });
70 }
71 res.json({ ok: true, q, kandidaten: await MusicBrainz.zoekArtiesten(q) });
72});
73
74/**
75 * De terug-weg: noemt de MusicBrainz-pagina ons domein? (shaer-mbz)
76 *
77 * Een koppeling van onze kant is een bewering -- iedereen kan een id typen.
78 * Pas als de artiestenpagina TERUGWIJST is het een paar. Wij zetten die
79 * verwijzing niet zelf: dat kan niet via hun API en hoort ook niet, de artiest
80 * doet dat op musicbrainz.org onder "social networking".
81 */
82router.get('/api/musicbrainz/terugweg', requireGod, async (req, res) => {
83 const mbid = String(req.query.mbid || '').trim().toLowerCase();
84 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
85 if (!isMbid(mbid) || !base) return res.json({ ok: true, verified: false, urls: [] });
86 res.json({ ok: true, ...(await MusicBrainz.controleerTerugweg(mbid, base)) });
87});
88
89// ==================== SAVE ====================
90router.post('/', requireGod, (req, res) => {
91 const primary = getPrimarySite();
92 if (!primary) return res.redirect('/admin/seo?error=' + encodeURIComponent('Geen site gevonden'));
93
94 const f = req.body;
95 const schemaType = f.schema_type === 'Organization' ? 'Organization' : 'Person';
96
97 db.prepare(`
98 UPDATE sites SET
99 robots_index = ?,
100 title_template = ?,
101 canonical = ?,
102 default_description = ?,
103 og_image_default = ?,
104 og_theme = ?,
105 og_locale = ?,
106 author = ?,
107 twitter = ?,
108 facebook_app_id = ?,
109 google_verification = ?,
110 bing_verification = ?,
111 pinterest_verification = ?,
112 yandex_verification = ?,
113 schema_type = ?,
114 publisher_name = ?,
115 publisher_url = ?,
116 publisher_logo = ?,
117 mb_artist_id = ?, mb_artist_name = ?,
118 updated_at = CURRENT_TIMESTAMP
119 WHERE id = ?
120 // De MusicBrainz-koppeling (shaer-mbz). Alleen een echte MBID komt de kolom
121 // in: zonder deze zeef sluipt er een URL of een handle in het veld dat naar
122 // buiten gaat, en het gaat naar TWEE uitgangen -- de JSON-LD en de actor.
123 // Leeg is een geldige keuze; dat is ontkoppelen.
124 const mbRuw = String(f.mb_artist_id || '').trim().toLowerCase();
125 const mbArtistId = isMbid(mbRuw) ? mbRuw : null;
126
127 `).run(
128 f.robots_index ? 1 : 0,
129 (f.title_template || '{title} — {site}').slice(0, 200),
130 trimOrNull(f.canonical, 200),
131 trimOrNull(f.default_description, 500),
132 trimOrNull(f.og_image_default, 500),
133 (f.og_theme === 'light' || f.og_theme === 'dark') ? f.og_theme : null, // null = auto (follow site theme)
134 trimOrNull(f.og_locale, 32),
135 trimOrNull(f.author, 120),
136 trimOrNull(f.twitter, 64),
137 trimOrNull(f.facebook_app_id, 64),
138 trimOrNull(f.google_verification, 200),
139 trimOrNull(f.bing_verification, 200),
140 trimOrNull(f.pinterest_verification, 200),
141 trimOrNull(f.yandex_verification, 200),
142 schemaType,
143 trimOrNull(f.publisher_name, 200),
144 trimOrNull(f.publisher_url, 200),
145 trimOrNull(f.publisher_logo, 500),
146 mbArtistId,
147 mbArtistId ? (String(f.mb_artist_name || '').trim().slice(0, 200) || null) : null,
148 primary.id,
149 );
150
151 res.redirect('/admin/seo?success=' + encodeURIComponent('SEO-instellingen opgeslagen'));
152});
153
154export default router;
Note: See TracBrowser for help on using the repository browser.