fix: GatewayLink wijst naar ipfs.io in plaats van de eigen gateway

- Component hardcodede https://ipfs.io/ipfs/<cid>; de portal heeft zijn eigen
  gateway (NEXT_PUBLIC_GATEWAY_URL, fallback ipfs.maos.dedyn.io)
- Bouwt de link nu via de gedeelde gatewayLink() uit lib/helpers, dus zelfde
  basis-URL en /ipfs/-pad als alle andere share-links
- ?filename= gaat dezelfde route in, encoding ongewijzigd
- Nieuwe render-test (react-dom/server) dekt gateway-keuze, config-override
  en filename-query
This commit is contained in:
maikrolf
2026-10-01 19:21:01 +02:00
parent 4a5b146713
commit 0279282f7e
2 changed files with 57 additions and 3 deletions
+7 -3
View File
@@ -2,6 +2,7 @@
import { useState } from 'react';
import { Copy, CheckCircle } from 'lucide-react';
import { gatewayLink } from '@/lib/helpers';
interface GatewayLinkProps {
cid: string;
@@ -10,11 +11,14 @@ interface GatewayLinkProps {
export default function GatewayLink({ cid, filename }: GatewayLinkProps) {
const [copied, setCopied] = useState(false);
const gatewayUrl = `https://ipfs.io/ipfs/${cid}${filename ? `?filename=${encodeURIComponent(filename)}` : ''}`;
// Gedeelde helper: gateway-basis uit lib/config.ts + het /ipfs/-pad van de
// route GET /ipfs/<cid>. Nooit een externe gateway (ipfs.io) hardcoden —
// dan wijst de link content van een andere node aan.
const link = gatewayLink(cid, filename);
async function handleCopy() {
try {
await navigator.clipboard.writeText(gatewayUrl);
await navigator.clipboard.writeText(link);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
@@ -25,7 +29,7 @@ export default function GatewayLink({ cid, filename }: GatewayLinkProps) {
return (
<div className="flex items-center gap-2 text-xs">
<code className="flex-1 truncate text-surface-400 bg-surface-900 px-2 py-1 rounded">
{gatewayUrl}
{link}
</code>
<button
onClick={handleCopy}
@@ -0,0 +1,50 @@
import { describe, it, expect, afterEach } from 'vitest';
import { renderToStaticMarkup } from 'react-dom/server';
import GatewayLink from '../GatewayLink';
/* ── GatewayLink ──
*
* Audit: de component hardcodede `https://ipfs.io/ipfs/<cid>`, terwijl de
* portal zijn EIGEN gateway heeft (NEXT_PUBLIC_GATEWAY_URL, fallback
* https://ipfs.maos.dedyn.io). De gedeelde helper `gatewayLink()` uit
* `@/lib/helpers` bouwt de link — deze tests bewaken dat de component die
* ook echt gebruikt in plaats van een eigen URL te bouwen.
*/
const ORIGINAL_GATEWAY = process.env.NEXT_PUBLIC_GATEWAY_URL;
afterEach(() => {
if (ORIGINAL_GATEWAY === undefined) delete process.env.NEXT_PUBLIC_GATEWAY_URL;
else process.env.NEXT_PUBLIC_GATEWAY_URL = ORIGINAL_GATEWAY;
});
function render(cid: string, filename?: string): string {
return renderToStaticMarkup(<GatewayLink cid={cid} filename={filename} />);
}
describe('GatewayLink', () => {
it('wijst naar de eigen gateway van de portal, niet naar ipfs.io', () => {
const html = render('QmTest123');
expect(html).toContain('https://ipfs.maos.dedyn.io/ipfs/QmTest123');
expect(html).not.toContain('ipfs.io');
});
it('volgt NEXT_PUBLIC_GATEWAY_URL', () => {
process.env.NEXT_PUBLIC_GATEWAY_URL = 'https://gateway.example.org';
expect(render('QmTest123')).toContain('https://gateway.example.org/ipfs/QmTest123');
});
it('toont de CID op het /ipfs/-pad', () => {
expect(render('QmTest123')).toContain('/ipfs/QmTest123');
});
it('zet filename als query-parameter, url-encoded', () => {
expect(render('QmTest123', 'rap portaal.txt')).toContain(
'?filename=rap%20portaal.txt',
);
});
it('laat de query-string weg als er geen filename is', () => {
expect(render('QmTest123')).not.toContain('?filename=');
});
});