MonitoRSS Documentation

repository·main·Indexed 22 days ago

https://github.com/synzen/monitorss

MonitoRSS (formerly Discord.RSS) is a service that delivers highly-customized news feeds to Discord servers. The documentation covers self-hosting via Docker and Docker Compose, environment variable configuration for Discord, Reddit, and SMTP, and developer guides for E2E testing with Playwright and RabbitMQ messaging using the @monitorss/contracts package.

Tokens
84.6K
Snippets
76
Records
436
Agent score
79%

What's inside MonitoRSS

  1. Overview of Discord-REST-Listener

    main
    Discord-REST-Listener is a queueing service designed to handle incoming Discord API requests (such as messages sent to servers). Its primary purpose is to manage and process rate-limiting information returned by Discord, ensuring requests are handled according to Discord's constraints. It is specifically designed for use with MonitoRSS and is intended to work in conjunction with MonitoRSS/Discord-REST.
  2. Understand the Frontend Architecture Decision Records (ADRs)

    main

    The services/backend-api/client/docs/adr/ directory contains Architecture Decision Records specifically for the React application served by the backend-api. These records document the architectural constraints, patterns, and trade-offs used in the frontend.

    Key Architectural Patterns documented in ADRs:

    • Folder Model: Uses a 'thin pages, fat features' approach with destination sub-features and a narrow shared base.
    • State Ownership:
      • React Query: Used for server state.
      • URL: Used for shareable state.
      • Context: Used only for cross-cutting concerns.
    • Workspace Scoping: Uses an implicit /me route and slug-based /workspaces/:workspaceSlug/... routes.
    • Styling: Implements a semantic role system, encoding mechanisms, and a contrast gate.
    • Destination Extensibility: Maintains the FeedConnectionType shell by using destination sub-features.
    • Fitness Functions: Enforced via three specific ESLint architecture rules.
  3. Understand Workspace URL scoping with slugs

    main

    MonitoRSS uses slug-based URLs for workspace scoping instead of opaque IDs. This allows for readable and shareable URLs such as /workspaces/acme-marketing/feeds.

    When interacting with the API or building client-side routes, use the workspaceSlug rather than a workspaceId. The routing structure follows the pattern /workspaces/:workspaceSlug.

    Key technical details:

    • Route Shape: /workspaces/:workspaceSlug/...
    • Route Type: RouteScope includes { workspaceSlug?: string }.
    • Conflict Handling: If a slug is already in use, the backend returns the WORKSPACE_SLUG_TAKEN error code.
    • Validation: Slugs follow the SLUG_PATTERN and SLUG_MAX constraints defined in the system's shared validation logic.
  4. Understand the Workspace Scoping architecture

    main

    MonitoRSS uses a hybrid URL scoping model to support both personal and workspace-owned resources. This design allows users to manage feeds individually or within shared workspaces (similar to Slack or Notion).

    URL Structure

    • Personal Scope (Default): Uses unprefixed routes. These are the current standard and are treated as implicit /me/ routes.
      • /feeds (List of personal feeds)
      • /feeds/:feedId (Specific feed)
      • /settings
    • Workspace Scope: Uses a /workspaces/:workspaceSlug/ prefix.
      • /workspaces/:workspaceSlug/feeds
      • /workspaces/:workspaceSlug/settings

    Mental Model

    • Workspaces are application-owned containers that can integrate with multiple Discord guilds. They are distinct from Discord Servers/Guilds, which are external entities.
    • Personal feeds are user-scoped.
    • Workspace feeds are owned by the workspace, and membership is managed at the workspace level.
  5. Access dependencies via `request.container`

    main

    The backend-api uses request.container as a service locator pattern. Every Fastify request carries the full DI container, allowing handlers to access required services.

    Usage Pattern: Handlers access repositories or services directly from the request object:

    // Example of accessing a repository via the container in a handler
    const userRepo = request.container.userFeedRepository;
    const user = await userRepo.findById(id);

    Note on DI Patterns: While request.container is the standard, new routes may implement a 'dependency-injected route-plugin' pattern where the registration function receives a typed deps object. When modifying existing code, match the existing pattern used in that specific handler.

  6. Workspace feed ownership and quotas

    main

    Feeds can be personal or owned by a workspace. A feed belongs to a workspace if its workspaceId is set to a valid ObjectId; otherwise, it is personal (workspaceId: null).

    Key Behaviors:

    • Authorization: Workspace-feed operations require the caller to be a member of that workspace.
    • Quota Enforcement: Workspace feeds are governed by getWorkspaceBenefits(workspaceId). They are isolated from personal supporter perks (like refresh rates or personal feed counts).
    • Self-Hosting: On self-hosted instances, workspace feeds are unlimited unless the operator sets BACKEND_API_DEFAULT_MAX_WORKSPACE_FEEDS.
    • Billing: For hosted instances, workspace feed caps are determined by the workspace's subscription tier via Paddle.
  7. Understand the MonitoRSS frontend styling system

    main

    The MonitoRSS frontend (located in services/backend-api/client/) uses a semantic role system rather than hardcoded color values. This system is designed to ensure accessibility (contrast compliance), maintainability (single-file reskinning), and extensibility (easy theme changes).

    Instead of referencing raw palette colors (e.g., gray.800), developers must use semantic roles. This prevents the 'incoherent surface' problem where different parts of the UI use slightly different shades of the same color, and ensures that controls remain visible and accessible across different themes.

  8. Architectural rules for `user-feeds-next` development

    main

    When contributing to or extending the user-feeds-next service, adhere to these structural rules to maintain pipeline integrity:

    • No Discord types in articles/: The articles/ module must remain platform-agnostic. Never import Discord-specific types (like DiscordMessageApiPayload) into articles/ or formatting/.
    • Formatting is per-medium: Do not move formatting to the pipeline orchestrator. Formatting (e.g., formatArticleForDiscord) should be called within the per-medium delivery loop in delivery-routing.ts. This allows each medium to have unique formatter options and custom placeholders.
    • Strict Import Boundaries: Only the pipeline/ module should import from both articles/ and delivery/. Other modules must not reach across this boundary.
    • Centralized Stores: Do not distribute persistence logic into domain modules; keep all database and cache interactions within stores/.
  9. Rules for the `src/shared/` layer

    main

    To maintain architectural integrity and prevent circular dependencies, the src/shared/ layer must adhere to the following rules:

    1. No Feature Imports: Modules in src/shared/** are strictly forbidden from importing from @/features/* or any **/features/* paths. This is enforced via no-restricted-imports linting rules.
    2. No Deep Imports: Consumers must import from the concern's barrel file (e.g., @/shared/concernName) rather than reaching into internal files (e.g., @/shared/concernName/file.ts).
    3. Reactive Promotion Only: Do not add code to src/shared/ speculatively. A module should only be moved to this layer once it is actually required by two or more unrelated features.
  10. Understand the MonitoRSS Folder Model

    main

    The MonitoRSS backend-api uses a three-layer folder model designed to enforce feature ownership and minimize a bloated shared base. When deciding where to place a new file, use this 3-question lookup:

    1. Is it a route shell? → Place in pages/.
    2. Is it about one feature's domain? → Place in features/<feature>/.
    3. Is it generic AND non-feature-coupled? → Place in the top-level base.

    Key Principles:

    • Thin Pages: pages/ should only contain thin shells (composition) for routes. Complex logic and JSX trees must move into a feature.
    • Fat Features: Features own their components, contexts, editors, and panels. This ensures feature ownership is real and easy to locate.
    • Narrow Shared Base: Only genuinely generic, non-feature-coupled code lives in the top-level base. Avoid the "I'll just put it in components/ for now" pattern.
    • Deliberate Reuse: Cross-feature code reuse should be avoided if the case is small (prefer duplication). If reuse is necessary, the code must be explicitly lifted to a shared location.
  11. Standard tooling for new MonitoRSS services

    main

    When developing new services within the MonitoRSS ecosystem, you should follow the standard stack to ensure maintainability and architectural consistency. The canonical reference implementation for this stack is found in services/user-feeds-next/.

    Standard Stack Requirements:

    • HTTP Framework: Use plain Fastify. Do not use NestJS wrappers or decorators.
    • Persistence: Use raw pg for PostgreSQL. Use Mongoose only if the service specifically requires MongoDB. Do not use MikroORM for new services.
    • RabbitMQ: Use rabbitmq-client (the Node-native library). Do not use @golevelup/nestjs-rabbitmq or amqplib/amqp-connection-manager.
    • Validation: Use Zod for validating request bodies, event payloads, and environment configurations.
    • Worker Pool: Use workerpool for handling CPU-bound tasks.