Class: OKF::Server::Hub

Inherits:
Object
  • Object
show all
Defined in:
lib/okf/server/hub.rb

Overview

Multiplexes N bundles behind one server. Each bundle is mounted at /b// and served by its own OKF::Server::App; / redirects to the default bundle (explicitly chosen, or the first), or shows an empty-state page when none are registered. The graph page is already mount-relative (its fetch endpoints are relative), so hosting under a prefix needs only a clean PATH_INFO strip here plus a trailing-slash redirect. Part of the shell — it is a Rack app.

GET /               302 -> /b/<default>/   (empty-state page when no bundles)
GET /b/             the bundle index — every hosted bundle, default marked
GET /b/<slug>       301 -> /b/<slug>/      (query string preserved)
GET /b/<slug>/...   delegated to that bundle's App (the prefix stripped)
GET (unknown slug)  404 as a page listing the hosted bundles — a stale
                  bookmark after a rename gets a way home, not bare text

bundles is an ordered array of Hub::Bundle (slug, folder, title). Apps are built up front, each carrying the other bundles as siblings so the in-page switcher can jump between them; static okf render files get no siblings and so cannot switch.

Defined Under Namespace

Classes: Bundle

Constant Summary collapse

MOUNT =
"/b"
STYLE =

Shared style for the hub's own pages (empty landing, /b/ index, 404) — self-contained and theme-aware, no external requests, in keeping with the graph page's own no-CDN-at-rest rule.

<<~CSS
  body{margin:0;min-height:100vh;display:grid;place-items:center;background:#f4f5f7;color:#1f2328;font:15px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
  main{max-width:34rem;width:calc(100% - 4rem);padding:2rem}h1{font-size:1.3rem;margin:0 0 .5rem}
  code{background:#e6e8eb;padding:.15rem .4rem;border-radius:.35rem}
  ul.bundles{list-style:none;margin:1rem 0 0;padding:0}
  ul.bundles li{padding:.45rem 0;border-top:1px solid #e6e8eb;display:flex;justify-content:space-between;gap:1rem;align-items:baseline}
  ul.bundles a{color:inherit;font-weight:600;text-decoration:none}ul.bundles a:hover{text-decoration:underline}
  .meta{color:#63697a;font-size:.85rem;white-space:nowrap}
  .def{margin-left:.5rem;padding:.05rem .45rem;border-radius:99px;background:#e6e8eb;font-size:.75rem}
  @media(prefers-color-scheme:dark){body{background:#111318;color:#eceef1}code,.def{background:#232833}
  ul.bundles li{border-color:#2a2e36}.meta{color:#9aa0aa}}
CSS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bundles, layout: "cose") ⇒ Hub

The first bundle is the one / redirects to — the registry hands them over in its own order, where first is the default (okf registry default moves an entry to the front), and an ephemeral run takes the dirs as typed.



58
59
60
61
62
# File 'lib/okf/server/hub.rb', line 58

def initialize(bundles, layout: "cose")
  @bundles = bundles
  @default = bundles.first
  @apps = build_apps(layout)
end

Instance Attribute Details

#bundlesObject (readonly)

The hosted bundles in mount order, and the one / redirects to — so a caller printing the mount table asks the hub instead of re-deriving the rule and drifting from it.



53
54
55
# File 'lib/okf/server/hub.rb', line 53

def bundles
  @bundles
end

#defaultObject (readonly)

The hosted bundles in mount order, and the one / redirects to — so a caller printing the mount table asks the hub instead of re-deriving the rule and drifting from it.



53
54
55
# File 'lib/okf/server/hub.rb', line 53

def default
  @default
end

Instance Method Details

#call(env) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/okf/server/hub.rb', line 64

def call(env)
  request = Rack::Request.new(env)
  return not_found unless request.get?

  path = request.path_info
  query = request.query_string.to_s
  # Everything this class *emits* must carry the prefix a host mounted it
  # under; PATH_INFO is already relative to it.
  base = env["SCRIPT_NAME"].to_s
  return landing(base, query) if [ "", "/" ].include?(path)
  return html(200, index_page(base)) if [ MOUNT, "#{MOUNT}/" ].include?(path)

  slug, rest = split(path)
  app = slug && @apps[slug]
  return html(404, missing_page(base, path)) unless app
  return redirect("#{base}#{MOUNT}/#{slug}/", 301, query) if rest.empty?

  app.call(mounted(env, slug, rest))
end