Maileditor Docs

Vue

A Nuxt server route that signs the user id and a page component that renders the iframe.

Nuxt gives a Vue app its own server, so signing lives in a server route and the page fetches the signed values with useFetch. The Plugin Secret stays in private runtime config.

If you use Vue without Nuxt, keep the page component below, replace useFetch with fetch, and sign with any of the server routes from the other guides, for example the Express route in the React guide.

Before you start

Follow the first three steps of the quickstart: create a plugin, add localhost:3000 to its allowed domains, and copy the Plugin ID and Secret.

Configuration

Keys placed directly under runtimeConfig are private and only available on the server.

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    maileditorPluginId: '',
    maileditorPluginSecret: '',
    maileditorEditorBaseUrl: 'https://maileditor.net',
    maileditorPluginVersion: '1.0.0',
  },
})

Nuxt fills them from environment variables with the NUXT_ prefix:

.env
NUXT_MAILEDITOR_PLUGIN_ID=9f3c1e7a-4b21-4c8e-9a77-1d2f5b6c8e10
NUXT_MAILEDITOR_PLUGIN_SECRET=your_plugin_secret

Sign on the server

server/api/auth.get.ts
import { createHmac } from 'node:crypto'

export default defineEventHandler((event) => {
  const { user } = getQuery(event)
  if (typeof user !== 'string' || user === '') {
    throw createError({ statusCode: 400, statusMessage: 'user is required' })
  }

  const config = useRuntimeConfig(event)
  const expires = Math.floor(Date.now() / 1000) + 300
  const payload = `v1|${Buffer.byteLength(user)}:${user}|${expires}`
  const signature = createHmac('sha256', config.maileditorPluginSecret)
    .update(payload)
    .digest('hex')

  return {
    externalUserId: user,
    identityExpires: expires,
    identitySignature: signature,
    pluginId: config.maileditorPluginId,
    pluginVersion: config.maileditorPluginVersion,
    editorBaseUrl: config.maileditorEditorBaseUrl,
  }
})

In a real app, take the user id from the caller's session instead of trusting the user query parameter.

Render the iframe

pages/editor.vue
<script setup lang="ts">
const { data: auth } = await useFetch('/api/auth', {
  query: { user: 'customer-user-4471' },
})

const src = computed(() => {
  if (!auth.value) return undefined
  const params = new URLSearchParams({
    external_user_id: auth.value.externalUserId,
    identity_expires: String(auth.value.identityExpires),
    identity_signature: auth.value.identitySignature,
    plugin_version: auth.value.pluginVersion,
  })
  return `${auth.value.editorBaseUrl}/plugin-editor/v1/${auth.value.pluginId}?${params}`
})
</script>

<template>
  <iframe
    v-if="src"
    :src="src"
    title="Email editor"
    allow="clipboard-write; fullscreen"
    style="width: 100%; height: 100vh; border: 0"
  />
</template>

useFetch runs on the server during the first render and in the browser on client-side navigation, so the signature is fresh either way.

Next steps

  • Add template_id to the query string to open one of the user's saved templates. See Embedding the editor.
  • If the iframe shows "Plugin editor unavailable", read result.error in the session response. See Error codes.

On this page