Skip to content

aayushmau5/event_horizon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

99 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Event Horizon

A personal website and blog built with Phoenix, LiveView, and MDEx. Migrated from NextJS to Phoenix to leverage real-time features and Elixir's distributed capabilities.

Live site: aayushsahu.com


Overview

Event Horizon is a Phoenix application that serves as a personal website with:

  • Static blog publishing via NimblePublisher and MDEx
  • Dynamic LiveView components embedded directly in markdown
  • Real-time analytics through clustered Elixir nodes
  • Cross-node presence tracking for live visitor counts
  • Cluster latency monitoring across distributed fly.io regions

Architecture

flowchart TB
    subgraph Client
        Browser[Browser]
    end

    subgraph EventHorizon["Event Horizon (This App)"]
        Endpoint[Phoenix Endpoint]
        Router[Router]
        LiveViews[LiveViews]
        Blog[Blog Module]
        Presence[Presence Tracker]
        Latency[Latency Monitor]
    end

    subgraph BlogPipeline["Blog Pipeline"]
        Markdown[Markdown Files]
        Parser[NimblePublisher]
        MDEx[MDEx Parser]
        Plugin[MDEx Plugin]
        HEEX[HEEX Components]
    end

    subgraph Cluster["Fly.io Cluster"]
        Accumulator[Accumulator Node]
        Battleship[Battleship Node]
    end

    Browser --> Endpoint
    Endpoint --> Router
    Router --> LiveViews
    LiveViews --> Blog
    LiveViews --> Presence
    LiveViews --> Latency

    Markdown --> Parser
    Parser --> MDEx
    MDEx --> Plugin
    Plugin --> HEEX
    Blog --> Parser

    Presence <-.-> |PubSub| Accumulator
    Latency <-.-> |ERPC| Accumulator
    Latency <-.-> |ERPC| Battleship
    Accumulator <-.-> |PubSub| Battleship
Loading

Blog System

The blog uses a compile-time publishing system that parses markdown into optimized HTML with embedded Phoenix components.

flowchart LR
    subgraph Input
        MD[".md files"]
        Frontmatter[YAML Frontmatter]
    end

    subgraph Processing
        NP[NimblePublisher]
        Parser[Custom Parser]
        MDEx[MDEx]
        Plugin[MDEx Plugin]
    end

    subgraph Transforms
        BQ[Blockquotes β†’ Callouts]
        Links[Links β†’ styled_anchor]
        Code[Code β†’ Syntax Highlight]
        Images[Images β†’ Lazy Load]
    end

    subgraph Output
        Static[Static HTML]
        Dynamic[Dynamic LiveComponents]
    end

    MD --> NP
    Frontmatter --> NP
    NP --> Parser
    Parser --> MDEx
    MDEx --> Plugin
    Plugin --> BQ & Links & Code & Images
    BQ & Links & Code & Images --> Static & Dynamic
Loading

Dynamic Markdown

Blogs can embed interactive LiveView components directly in markdown:

Here's a counter:

<.counter id="my-counter" />

The dynamic: true frontmatter flag enables LiveView rendering for that blog post.

Real-time Features

PubSub Contract

Communication between clustered nodes uses typed message structs via PubSubContract:

sequenceDiagram
    participant EH as Event Horizon
    participant PS as Phoenix.PubSub
    participant ACC as Accumulator

    Note over EH,ACC: Analytics Flow
    EH->>PS: SiteVisit / BlogVisit
    PS->>ACC: (distributed)
    ACC->>PS: SiteUpdated / BlogUpdated
    PS->>EH: (distributed)

    Note over EH,ACC: Presence Flow
    EH->>PS: SitePresence / BlogPresence
    PS->>ACC: (distributed)
    ACC->>PS: PresenceRequest
    PS->>EH: (distributed)
Loading

Cluster Latency

The Latency GenServer continuously measures RTT between nodes using :erpc:

flowchart LR
    EH[Event Horizon<br/>Frankfurt]
    PHX[Accumulator<br/>Frankfurt]
    BSH[Battleship<br/>Frankfurt]

    EH <-->|erpc.call| PHX
    EH <-->|erpc.call| BSH
    PHX <-->|erpc.call| BSH
Loading

View live latencies at /cluster.

Project Structure

lib/
β”œβ”€β”€ event_horizon/
β”‚   β”œβ”€β”€ blog/
β”‚   β”‚   β”œβ”€β”€ article.ex       # Article struct
β”‚   β”‚   β”œβ”€β”€ converter.ex     # HTML conversion
β”‚   β”‚   β”œβ”€β”€ mdex_plugin.ex   # AST transformations
β”‚   β”‚   └── parser.ex        # Frontmatter parsing
β”‚   β”œβ”€β”€ blog.ex              # NimblePublisher config
β”‚   β”œβ”€β”€ presence.ex          # Phoenix.Presence for visitors
β”‚   β”œβ”€β”€ latency.ex           # Cross-node latency GenServer
β”‚   β”œβ”€β”€ pubsub_contract.ex   # Typed PubSub messages
β”‚   β”œβ”€β”€ rss.ex               # RSS feed generation
β”‚   └── sitemap.ex           # Sitemap generation
β”œβ”€β”€ event_horizon_web/
β”‚   β”œβ”€β”€ components/          # HEEX components
β”‚   β”œβ”€β”€ live/                # LiveView modules
β”‚   β”‚   β”œβ”€β”€ blog_live/       # Blog pages
β”‚   β”‚   β”œβ”€β”€ cluster_live.ex  # Cluster status page
β”‚   β”‚   └── ...
β”‚   └── router.ex
└── mix/                     # Custom mix tasks
priv/
└── posts/                   # Markdown blog posts

Getting Started

Prerequisites

  • Elixir 1.18+
  • Erlang/OTP 27+

Development

# Install dependencies and setup
mix setup

# Start the server
mix phx.server

# Or with IEx
iex -S mix phx.server

Visit localhost:4000.

Mix Tasks

Task Description
mix setup Install deps, build assets, generate RSS and sitemap
mix rss.generate Generate RSS feed
mix sitemap.generate Generate sitemap.xml
mix precommit Run compile checks, format, and tests

Deployment

Deployed on fly.io with DNS clustering enabled. The app automatically connects to other nodes in the same organization.

fly deploy

Dependencies

Key libraries:

License

MIT

About

🌌 A digital garden

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors