Skip to main content
The Legal Service manages legal content pages including privacy policy and terms of service documents.

Overview

This service provides methods to retrieve and update legal pages using a slug-based identification system.

Dependencies

  • legalRepository - Database operations for legal pages

Methods

getLegalPages()

Retrieves both privacy and terms pages.
import { legalService } from '$lib/server/services/legal.service.js';

const { privacidad, terminos } = await legalService.getLegalPages();
privacidad
object | null
Privacy policy page object:
  • id: Page identifier
  • slug: Page slug (‘privacidad’)
  • title: Page title
  • content: Page content (HTML or markdown)
terminos
object | null
Terms of service page object:
  • id: Page identifier
  • slug: Page slug (‘terminos’)
  • title: Page title
  • content: Page content (HTML or markdown)

Example Response

{
  privacidad: {
    id: 1,
    slug: 'privacidad',
    title: 'Política de Privacidad',
    content: '<h1>Política de Privacidad</h1><p>...</p>'
  },
  terminos: {
    id: 2,
    slug: 'terminos',
    title: 'Términos y Condiciones',
    content: '<h1>Términos y Condiciones</h1><p>...</p>'
  }
}
Returns null for pages that don’t exist.

Implementation Details

Source: src/lib/server/services/legal.service.js:4-10
const pages = await legalRepository.getAll();
const privacidad = pages.find((p) => p.slug === 'privacidad') || null;
const terminos = pages.find((p) => p.slug === 'terminos') || null;
return { privacidad, terminos };

getBySlug()

Retrieves a specific legal page by its slug.
import { legalService } from '$lib/server/services/legal.service.js';

const privacyPage = await legalService.getBySlug('privacidad');
const termsPage = await legalService.getBySlug('terminos');
slug
string
required
Page slug. Typically 'privacidad' or 'terminos'
page
object | null
Legal page object or null if not found:
  • id: Page identifier
  • slug: Page slug
  • title: Page title
  • content: Page content

Implementation Details

Source: src/lib/server/services/legal.service.js:13-15

updateLegalPage()

Updates or creates a legal page (upsert by slug).
import { legalService } from '$lib/server/services/legal.service.js';

await legalService.updateLegalPage('privacidad', {
  title: 'Política de Privacidad',
  content: `
    <h1>Política de Privacidad</h1>
    <p>En PROVESA respetamos tu privacidad...</p>
    <h2>Recolección de Datos</h2>
    <p>Recolectamos los siguientes datos...</p>
  `
});
slug
string
required
Page slug to update. Use 'privacidad' or 'terminos'
title
string
required
Page title
content
string
required
Page content (HTML or markdown)
This is an upsert operation. If the page doesn’t exist, it will be created. If it exists, it will be updated.

Implementation Details

Source: src/lib/server/services/legal.service.js:18-20

Usage Examples

// src/routes/admin/legal/+page.server.js
import { legalService } from '$lib/server/services/legal.service.js';
import { fail } from '@sveltejs/kit';

export async function load() {
  const { privacidad, terminos } = await legalService.getLegalPages();
  return { privacidad, terminos };
}

export const actions = {
  updatePrivacy: async ({ request }) => {
    const formData = await request.formData();
    
    try {
      await legalService.updateLegalPage('privacidad', {
        title: formData.get('title'),
        content: formData.get('content')
      });
      
      return { success: true };
    } catch (error) {
      return fail(500, { error: error.message });
    }
  },
  
  updateTerms: async ({ request }) => {
    const formData = await request.formData();
    
    try {
      await legalService.updateLegalPage('terminos', {
        title: formData.get('title'),
        content: formData.get('content')
      });
      
      return { success: true };
    } catch (error) {
      return fail(500, { error: error.message });
    }
  }
};
// src/routes/legal/[slug]/+page.server.js
import { legalService } from '$lib/server/services/legal.service.js';
import { error } from '@sveltejs/kit';

export async function load({ params }) {
  const page = await legalService.getBySlug(params.slug);
  
  if (!page) {
    throw error(404, 'Página no encontrada');
  }
  
  return { page };
}
<!-- src/routes/legal/[slug]/+page.svelte -->
<script>
  export let data;
</script>

<article class="legal-page">
  <h1>{data.page.title}</h1>
  <div class="content">
    {@html data.page.content}
  </div>
</article>

Admin Editor with Rich Text

<!-- src/routes/admin/legal/+page.svelte -->
<script>
  import { enhance } from '$app/forms';
  export let data;
  export let form;
  
  let activeTab = 'privacidad';
</script>

<div class="legal-editor">
  <div class="tabs">
    <button 
      class:active={activeTab === 'privacidad'}
      on:click={() => activeTab = 'privacidad'}
    >
      Privacidad
    </button>
    <button 
      class:active={activeTab === 'terminos'}
      on:click={() => activeTab = 'terminos'}
    >
      Términos
    </button>
  </div>
  
  {#if activeTab === 'privacidad'}
    <form method="POST" action="?/updatePrivacy" use:enhance>
      <div>
        <label for="privacy-title">Título</label>
        <input
          type="text"
          id="privacy-title"
          name="title"
          value={data.privacidad?.title || ''}
          required
        />
      </div>
      
      <div>
        <label for="privacy-content">Contenido</label>
        <textarea
          id="privacy-content"
          name="content"
          rows="20"
          required
        >{data.privacidad?.content || ''}</textarea>
      </div>
      
      <button type="submit">Guardar Privacidad</button>
    </form>
  {:else}
    <form method="POST" action="?/updateTerms" use:enhance>
      <div>
        <label for="terms-title">Título</label>
        <input
          type="text"
          id="terms-title"
          name="title"
          value={data.terminos?.title || ''}
          required
        />
      </div>
      
      <div>
        <label for="terms-content">Contenido</label>
        <textarea
          id="terms-content"
          name="content"
          rows="20"
          required
        >{data.terminos?.content || ''}</textarea>
      </div>
      
      <button type="submit">Guardar Términos</button>
    </form>
  {/if}
  
  {#if form?.success}
    <p class="success">Cambios guardados exitosamente</p>
  {/if}
  
  {#if form?.error}
    <p class="error">{form.error}</p>
  {/if}
</div>

Data Structure

interface LegalPage {
  id: number;
  slug: 'privacidad' | 'terminos';
  title: string;
  content: string; // HTML or markdown
}

Page Slugs

SlugPurposeCommon Title (Spanish)
privacidadPrivacy policyPolítica de Privacidad
terminosTerms of serviceTérminos y Condiciones

Content Formats

HTML Content

await legalService.updateLegalPage('privacidad', {
  title: 'Política de Privacidad',
  content: `
    <section>
      <h2>1. Información que Recopilamos</h2>
      <p>Recopilamos información que usted nos proporciona directamente...</p>
    </section>
    <section>
      <h2>2. Cómo Usamos su Información</h2>
      <ul>
        <li>Para procesar sus pedidos</li>
        <li>Para mejorar nuestros servicios</li>
        <li>Para comunicarnos con usted</li>
      </ul>
    </section>
  `
});

Markdown Content

await legalService.updateLegalPage('terminos', {
  title: 'Términos y Condiciones',
  content: `
# Términos y Condiciones

## 1. Aceptación de Términos

Al acceder y utilizar este sitio web, usted acepta estar sujeto a estos términos...

## 2. Uso del Sitio

Usted se compromete a:
- Usar el sitio solo con fines lícitos
- No interrumpir el funcionamiento del sitio
- Respetar los derechos de propiedad intelectual
  `
});
If using markdown, you’ll need to render it as HTML on the frontend using a markdown parser.

Privacy Policy Template

const privacyTemplate = {
  title: 'Política de Privacidad',
  content: `
    <h1>Política de Privacidad</h1>
    
    <h2>1. Información que Recopilamos</h2>
    <p>Recopilamos información personal que usted nos proporciona voluntariamente cuando se registra en el sitio, realiza una compra, o se comunica con nosotros.</p>
    
    <h2>2. Cómo Usamos su Información</h2>
    <p>Utilizamos la información recopilada para:</p>
    <ul>
      <li>Procesar sus transacciones</li>
      <li>Mejorar nuestro sitio web</li>
      <li>Enviar correos electrónicos periódicos</li>
    </ul>
    
    <h2>3. Protección de la Información</h2>
    <p>Implementamos medidas de seguridad para mantener la seguridad de su información personal.</p>
    
    <h2>4. Divulgación a Terceros</h2>
    <p>No vendemos, comercializamos ni transferimos su información personal a terceros.</p>
    
    <h2>5. Consentimiento</h2>
    <p>Al usar nuestro sitio, usted consiente nuestra política de privacidad.</p>
  `
};

await legalService.updateLegalPage('privacidad', privacyTemplate);

Terms of Service Template

const termsTemplate = {
  title: 'Términos y Condiciones',
  content: `
    <h1>Términos y Condiciones</h1>
    
    <h2>1. Aceptación de Términos</h2>
    <p>Al acceder y usar este sitio web, usted acepta cumplir con estos términos y condiciones.</p>
    
    <h2>2. Uso del Sitio</h2>
    <p>Usted acepta usar el sitio solo para fines legales y de manera que no infrinja los derechos de otros.</p>
    
    <h2>3. Propiedad Intelectual</h2>
    <p>Todo el contenido de este sitio es propiedad de PROVESA y está protegido por leyes de derechos de autor.</p>
    
    <h2>4. Limitación de Responsabilidad</h2>
    <p>PROVESA no será responsable de daños derivados del uso o la imposibilidad de usar este sitio.</p>
    
    <h2>5. Modificaciones</h2>
    <p>Nos reservamos el derecho de modificar estos términos en cualquier momento.</p>
    
    <h2>6. Contacto</h2>
    <p>Si tiene preguntas sobre estos términos, contáctenos en contacto@provesa.com</p>
  `
};

await legalService.updateLegalPage('terminos', termsTemplate);

Best Practices

Always consult with legal professionals before publishing privacy policies or terms of service.
Use semantic HTML for better accessibility and SEO.
Include a “Last Updated” date in your legal page content.
Legal pages should be reviewed and updated regularly to comply with current laws and regulations.

Rich Text Editor Integration

For a better editing experience, integrate a rich text editor:
<script>
  import { Editor } from '@tiptap/core';
  import StarterKit from '@tiptap/starter-kit';
  import { onMount } from 'svelte';
  
  let editor;
  let editorElement;
  
  onMount(() => {
    editor = new Editor({
      element: editorElement,
      extensions: [StarterKit],
      content: data.privacidad?.content || '',
      onUpdate: ({ editor }) => {
        // Get HTML content
        const html = editor.getHTML();
        // Store in hidden input for form submission
      }
    });
    
    return () => {
      editor.destroy();
    };
  });
</script>

<div bind:this={editorElement}></div>

Compliance Considerations

GDPR Compliance

If serving EU users, ensure your privacy policy covers:
  • Data collection and usage
  • User rights (access, deletion, portability)
  • Cookie usage
  • Data retention policies
  • Contact information for privacy inquiries
Privacy Policy:
  1. Information Collection
  2. Information Usage
  3. Information Protection
  4. Cookie Policy
  5. Third-Party Disclosure
  6. User Rights
  7. Contact Information
Terms of Service:
  1. Acceptance of Terms
  2. Site Usage Rules
  3. Intellectual Property
  4. User Accounts
  5. Limitation of Liability
  6. Termination Rights
  7. Governing Law