Source: outbox.js

import express from 'express';
import dotenv from 'dotenv';

import { getOutboxPosts } from '../lib/account.js';

dotenv.config();

/**
 * Express.js router for handling requests related to the user's outbox.
 *
 * @typedef {Object} OutboxRouter
 * @property {Function} getOutbox - Route handler for retrieving the user's outbox posts.
 *
 * @example
 * // Example usage:
 * import { router as outboxRouter } from './outboxRouter';
 * app.use('/outbox', outboxRouter);
 */
export const router = express.Router();

// const {
//     DOMAIN
// } = process.env;

/**
 * Route handler for retrieving the user's outbox posts.
 *
 * @function
 * @async
 * @param {Object} req - Express.js request object.
 * @param {Object} res - Express.js response object.
 * @returns {Promise<void>} Resolves with the user's outbox collection or rejects with an error response.
 *
 * @throws {Error} Responds with a 400 Bad Request if the 'offset' query parameter is not a valid number.
 *
 * @example
 * // Example route:
 * // GET /outbox
 * outboxRouter.get('/', outboxHandlers.getOutbox);
 */
router.get('/', async (req, res) => {
  /**
   * The result object containing the total number of posts and an array of outbox posts.
   *
   * @type {Object}
   * @property {number} total - The total number of outbox posts.
   * @property {Array<Object>} posts - An array of outbox posts.
   */
  const { total, posts } = await getOutboxPosts(req.query.offset || 0);

  /**
   * The URL of the user's outbox.
   *
   * @type {string}
   */
  const outboxUrl = req.app.get('account').actor.outbox;

  /**
   * The representation of the outbox collection to be sent in the response.
   *
   * @type {Object}
   * @property {string} type - The type of the collection ('OrderedCollection' or 'OrderedCollectionPage').
   * @property {number} totalItems - The total number of items in the collection.
   * @property {string} id - The unique identifier for the collection.
   * @property {string} [first] - The URL of the first page of the collection.
   * @property {string} [partOf] - The URL of the main collection that this page is part of.
   * @property {string} [next] - The URL of the next page of the collection.
   * @property {string} [prev] - The URL of the previous page of the collection.
   * @property {Array<Object>} [orderedItems] - An array of ordered items in the collection.
   * @property {Array<string>} ['@context'] - The context of the collection.
   */
  const collection = {
    type: 'OrderedCollection',
    totalItems: total,
    id: outboxUrl,
    '@context': ['https://www.w3.org/ns/activitystreams']
  };

  // Check if the 'offset' query parameter is a valid number
  if (isNaN(req.query.offset)) {
    collection.first = `${outboxUrl}?offset=0`;
  } else {
    // Adjust collection properties for paginated results
    const offset = parseInt(req.query.offset);
    collection.type = 'OrderedCollectionPage';
    collection.id = `${outboxUrl}?offset=${offset}`;
    collection.partOf = outboxUrl;
    collection.next = `${outboxUrl}?offset=${offset + 10}`;
    // todo: stop at 0
    if (offset - 10 > 0) {
      collection.prev = `${outboxUrl}?offset=${offset - 10}`;
    } else {
      collection.first = `${outboxUrl}?offset=0`;
    }

    // Transform each post into an ordered item in the collection
    collection.orderedItems = posts;
    collection.orderedItems = collection.orderedItems.map(activity => {
      return {
        id: `${activity.id}/activity`,
        type: 'Create',
        actor: activity.attributedTo,
        published: activity.published,
        to: activity.to,
        cc: activity.cc,
        object: activity
      };
    });
  }

  // Send the outbox collection in the response
  res.json(collection);
});