Spreadsheet-UX op een echte relationele engine

main

Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.

4.9 kB Ruw bestand
import { DEFAULT_LOCALE, type Locale } from '@wellformed/shared';
import { config } from '../config.js';

/**
 * Outbound email, in the smallest shape that is still real.
 *
 * Two providers, one `fetch` each, no dependency and no template engine. Mail
 * from Wellformed is plain text on purpose: the three things it ever sends are a
 * link and a sentence explaining it, and plain text renders identically
 * everywhere, never lands in a spam filter for its markup, and cannot hide the
 * URL behind an anchor — which for a password reset is a security property.
 *
 * `send` never throws. A provider outage must not turn "reset your password"
 * into a 500 that also tells the caller whether the address exists.
 */
export interface Mail {
  to: string;
  subject: string;
  body: string;
}

export interface SendResult {
  sent: boolean;
  /** Set when a send was attempted and failed, for the log and nothing else. */
  error?: string;
}

export function emailEnabled(): boolean {
  return config.email.provider !== 'none';
}

export async function sendMail(mail: Mail): Promise<SendResult> {
  const { provider, apiKey, from } = config.email;
  if (provider === 'none') return { sent: false };

  const request =
    provider === 'resend'
      ? {
          url: 'https://api.resend.com/emails',
          headers: { authorization: `Bearer ${apiKey}` },
          body: { from, to: [mail.to], subject: mail.subject, text: mail.body },
        }
      : {
          url: 'https://api.postmarkapp.com/email',
          headers: { 'x-postmark-server-token': apiKey },
          body: { From: from, To: mail.to, Subject: mail.subject, TextBody: mail.body },
        };

  try {
    const response = await fetch(request.url, {
      method: 'POST',
      headers: { 'content-type': 'application/json', accept: 'application/json', ...request.headers },
      body: JSON.stringify(request.body),
      signal: AbortSignal.timeout(10_000),
    });
    if (!response.ok) {
      const detail = await response.text().catch(() => '');
      return { sent: false, error: `${provider} answered ${response.status}: ${detail.slice(0, 300)}` };
    }
    return { sent: true };
  } catch (error) {
    return { sent: false, error: error instanceof Error ? error.message : String(error) };
  }
}

/** Make a client-side path absolute for a message, which has no origin to ask. */
export function publicLink(path: string): string {
  return `${config.publicUrl}${path.startsWith('/') ? path : `/${path}`}`;
}

/**
 * The two messages, written per language rather than run through a translation
 * layer: two messages in two languages is four short texts, and a layer would
 * be bigger than all of them together. The account's own locale chooses;
 * an account that never chose gets English, same as the client's default.
 */
export function resetEmail(to: string, name: string, link: string, locale?: Locale | null): Mail {
  if ((locale ?? DEFAULT_LOCALE) === 'nl') {
    return {
      to,
      subject: 'Stel je Wellformed-wachtwoord opnieuw in',
      body: [
        `Hallo ${name},`,
        '',
        'Iemand heeft gevraagd het wachtwoord van dit Wellformed-account opnieuw in te',
        'stellen. Open deze link om een nieuw wachtwoord te kiezen:',
        '',
        link,
        '',
        'De link werkt één keer en verloopt na een uur. Heb je hier niet om gevraagd,',
        'dan kun je dit bericht negeren — er is niets veranderd en je huidige',
        'wachtwoord werkt nog gewoon.',
      ].join('\n'),
    };
  }
  return {
    to,
    subject: 'Reset your Wellformed password',
    body: [
      `Hello ${name},`,
      '',
      'Someone asked to reset the password for this Wellformed account. Open this link',
      'to choose a new one:',
      '',
      link,
      '',
      'The link works once and expires in an hour. If you did not ask for this you',
      'can ignore this message — nothing has changed, and your current password',
      'still works.',
    ].join('\n'),
  };
}

export function verifyEmail(to: string, name: string, link: string, locale?: Locale | null): Mail {
  if ((locale ?? DEFAULT_LOCALE) === 'nl') {
    return {
      to,
      subject: 'Bevestig je e-mailadres voor Wellformed',
      body: [
        `Hallo ${name},`,
        '',
        'Bevestig dit adres, zodat we je kunnen bereiken als je ooit niet meer kunt',
        'inloggen:',
        '',
        link,
        '',
        'De link werkt één keer en verloopt na een dag. Wellformed werkt hoe dan ook —',
        'dit is alleen om je later een wachtwoordreset te kunnen sturen.',
      ].join('\n'),
    };
  }
  return {
    to,
    subject: 'Confirm your Wellformed email address',
    body: [
      `Hello ${name},`,
      '',
      'Confirm this address so that you can be reached if you are ever locked out:',
      '',
      link,
      '',
      'The link works once and expires in a day. Wellformed works either way — this is',
      'only about being able to send you a password reset later.',
    ].join('\n'),
  };
}