obol

Owner: IIIlllIIIllI URL: git@git.0x00nyx.xyz:seb/obolserver.git

README.md

Obol - small static site server in Nim
=====================================

Obol serves a directory of JSON content and HTML templates with a single
self-contained binary. No dependencies outside Nim stdlib.

Features
--------
- declarative routing from config.json
- JSON content files, no schema enforcement
- minimal template engine (includes, blocks, if/range)
- static files from /static
- param routes with {param} data paths
- hot reload in DEV_MODE

Installation
------------
Build with Nim:
    nim c -d:release obol.nim

Usage
-----
1. Prepare a site directory (see layout below or `example-site/`).
2. Run the server:
       SITE_DIR=/path/to/site ./obol
   or build the server binary and run it from there.
3. Optional env:
   - PORT (default 8080)
   - SITE_DIR (default .)
   - DEV_MODE=1 (reload config/global on mtime change)

Layout
------
Each site directory follows this structure:
- config.json
- content/global.json
- content/pages/*.json
- templates/*.html
- static/

config.json
-----------
Example:
    {
      "site": {...},
      "navigation": [...],
      "pages": [
        {
          "path": "/docs",
          "template": "docs.html",
          "data": "docs.json"
        }
      ],
      "notFoundTemplate": "404.html"
    }

Rules:
- paths must be unique
- template names must exist in templates/
- data is optional; missing file -> empty object

Render Data
-----------
Every template gets a single JSON object:
    {
      "config":     config.json tree,
      "global":     content/global.json tree,
      "page":       current page data,
      "pageConfig": the page entry from config.json,
      "request":    {"path", "method", "params"}
    }

Template Approach
-----------------
Templates are plain HTML with a tiny, data-only template language. There is
no custom context and no helpers: everything comes from the render data above.
Includes and blocks are resolved at runtime and cached in production.

Template Syntax
---------------
- Variable: {{name}} (supports dotted paths, e.g. {{page.title}})
- Conditionals:
      {{if page.published}}
        ...
      {{else}}
        ...
      {{end}}
- Ranges (arrays/objects):
      {{range page.items}}
        {{name}}
      {{end}}
- Include another template file:
      {{template "header"}}   -> templates/header.html
- Define a named block:
      {{define "layout"}}
        ...
      {{end}}

Notes:
- include depth is capped at 10; circular chains error
- include data is always the merged render object (no custom context)

Routing
-------
- routes are defined only in config.json
- /static/* is served automatically when static/ exists
- /health is provided unless you define it
- :param segments are supported; /blog/:slug exposes request.params.slug
- exact routes win over parameterized, then fewer params, then declaration order

Data Paths
----------
- data accepts {param} placeholders: posts/{slug}.json
- missing params or files -> 404 (with log message)
- resolved paths are clamped to content/ (no traversal)

Error Handling
--------------
- missing route -> render notFoundTemplate or plain 404
- JSON parse failure -> 500, logged to stdout
- template failure -> 500, logged to stdout
- framework never crashes on user content

Philosophy
----------
What Obol does: map URL paths to JSON files and render them into HTML.