OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note / lib / routes / links.js

83 lines · javascript ref:
/**
 * Link, Backlink, Graph, Merge & Split Routes
 *
 * Phase 4 HTTP endpoints for interlinking and advanced operations.
 */

import { json, readJson } from '../response.js'
import { UnauthorizedError, ForbiddenError } from '../errors.js'

async function requireAuth (req, server, action, notebook) {
  const { authorized, auth, reason } = await server.authManager.authorizeRequest(req, action, notebook)
  if (!auth) throw new UnauthorizedError('Authentication required')
  if (!authorized) throw new ForbiddenError(reason, action)
  return auth
}

export function registerLinkRoutes (router, server) {
  const nm = server.noteManager
  const lm = server.linkManager

  // ==================== LINKS ====================

  // Get outgoing links from a note (open read)
  router.get('/api/notebooks/:notebook/notes/:id/links', async (req, res) => {
    const links = await lm.getLinks(req.params.notebook, req.params.id)
    json(res, { links })
  })

  // Get backlinks (open read)
  router.get('/api/notebooks/:notebook/notes/:id/backlinks', async (req, res) => {
    const backlinks = await lm.getBacklinks(req.params.notebook, req.params.id)
    json(res, { backlinks })
  })

  // Get link graph (open read)
  router.get('/api/graph', async (req, res) => {
    const idx = req.url.indexOf('?')
    let notebook = null
    if (idx >= 0) {
      const qs = req.url.substring(idx + 1)
      for (const pair of qs.split('&')) {
        const [k, v] = pair.split('=')
        if (k === 'notebook') notebook = decodeURIComponent(v)
      }
    }
    const graph = await lm.getGraph(notebook)
    json(res, graph)
  })

  // ==================== MERGE ====================

  // Merge two notes in the same notebook
  router.post('/api/notebooks/:notebook/merge', async (req, res) => {
    await requireAuth(req, server, 'write', req.params.notebook)
    const body = await readJson(req)

    if (!body.noteId1 || !body.noteId2) {
      json(res, { error: 'noteId1 and noteId2 are required' }, 400)
      return
    }

    const merged = await nm.mergeNotes(req.params.notebook, body.noteId1, body.noteId2, {
      title: body.title
    })
    json(res, merged)
  })

  // ==================== SPLIT ====================

  // Split a note at heading boundaries
  router.post('/api/notebooks/:notebook/notes/:id/split', async (req, res) => {
    await requireAuth(req, server, 'write', req.params.notebook)
    const body = await readJson(req)

    const created = await nm.splitNote(req.params.notebook, req.params.id, {
      delimiter: body.delimiter
    })
    json(res, { split: created.length > 0, parts: created })
  })
}

export default registerLinkRoutes