From 7acccc847e58ba5c04561890783bcd852df7e40b Mon Sep 17 00:00:00 2001 From: polwex Date: Wed, 16 Jul 2025 15:36:16 +0700 Subject: m --- CLAUDE.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dune | 2 +- flake.nix | 7 +++-- lib/router.ml | 15 ++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index efbbe42..2d367d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,11 +2,102 @@ This app is a WIP to implement a blog as a React webapp using Ocaml, mlx for JSX, Piaf for HTTP handling, Caqti to handle database queries, using Eio across the app for async. +## Current Status + +The project is successfully running as a server-side rendered React application in OCaml with the following features implemented: + +- **Database**: SQLite with schema for posts, comments, and votes +- **Routing**: Type-safe routing using the `routes` library +- **SSR React**: JSX-like syntax via mlx, server-side rendering +- **API Endpoints**: RESTful endpoints for posts, comments, and votes +- **Frontend**: Tailwind CSS styling with responsive design +- **Static Assets**: Serving CSS, images, and other static files ## Build the app To compile the app and see if the code is correct do: `dune clean && dune build` +## Running the app +```bash +# Build and run the server +dune exec bs5 + +# Or use the run script +./run.sh +``` + +## Tech Stack + +### Core +- **OCaml**: Primary language +- **Eio**: Async runtime for OCaml 5 +- **mlx**: JSX syntax for OCaml React components +- **dune**: Build system + +### Web Framework +- **Piaf**: HTTP server and client +- **React (server-side)**: Rendering React components on the server + +### Database +- **Caqti**: Database abstraction layer +- **SQLite**: Embedded database with WAL mode +- **Rapper**: Type-safe SQL queries with PPX + +### Styling +- **Tailwind CSS**: Utility-first CSS framework +- **Custom fonts**: Crimson Text and Inter fonts included + +### Development +- **Nix**: Development environment with flake.nix +- **ocaml-lsp-server**: Language server support +- **ocamlformat-mlx**: Code formatting for mlx files + +## Project Structure + +``` +├── lib/ # Core application code +│ ├── handler.ml # HTTP handlers +│ ├── router.ml # Route definitions +│ ├── html.ml # HTML rendering utilities +│ ├── post_handlers.ml # POST request handlers +│ ├── shared/ +│ │ └── query.ml # Database queries +│ └── pages/ +│ ├── BlogIndex.mlx # Main blog page React component +│ └── components/ # Reusable React components +├── assets/ # Static assets +│ ├── styles.css # Generated Tailwind CSS +│ ├── fonts/ # Web fonts +│ ├── board/ # Board/category icons +│ └── avatars/ # User avatars +├── bulkdata/ # Database files +│ ├── blog.db # SQLite database +│ └── schema.sql # Database schema +└── bin/ # Main executable +``` + +## Key Features Implemented + +### Database Schema +- **Posts**: id, title, content, date, tags, url +- **Comments**: id, content, date, tags, url, post_id, parent, author +- **Votes**: post_id, comment_id, user_id, vote_type + +### API Endpoints +- `GET /` - Main blog page with post previews +- `GET /posts` - Get all posts +- `GET /posts/:id` - Get specific post +- `GET /posts/:id/comments` - Get comments for a post +- `POST /posts` - Create new post +- `POST /comments` - Create new comment +- `POST /votes` - Create new vote +- `GET /assets/*` - Serve static assets + +### React Components +- **Layout**: Main page layout with HTML boilerplate +- **PostPreviews**: Displays list of blog post previews +- **Navbar**: Navigation component +- **SiteTitle**: Header component ## Things to take into account diff --git a/dune b/dune index 509d512..98d0c1e 100644 --- a/dune +++ b/dune @@ -3,4 +3,4 @@ (deps (glob_files_rec assets/*)) (action - (bash "sh tailwind.sh"))) + (bash "sh ~/code/ocaml/bs5/tailwind.sh"))) diff --git a/flake.nix b/flake.nix index 75cdd2a..e15957d 100644 --- a/flake.nix +++ b/flake.nix @@ -53,8 +53,9 @@ src = pkgs.fetchFromGitHub { owner = "polwex"; repo = "server-reason-react"; - rev = "d5dd436b0a447ff0a82f9a8d7a02f102139299a9"; - sha256 = "PsrOqZgdFIy5tGoLpS+hf9uz42vKJZbSvdWRW8MX604="; + # rev = "d5dd436b0a447ff0a82f9a8d7a02f102139299a9"; + rev = "f9955158d42f87a902ff32e05629a1a4d72c1d7f"; + sha256 = "EoG6fTW2mAGge/hm0Z0mQOyxE2zIEXvRnu2KtUfWEqw="; }; propagatedBuildInputs = with pkgs.ocamlPackages; [ @@ -166,6 +167,8 @@ # }; in { devShells.default = pkgs.mkShell rec { + ANTHROPIC_BASE_URL = "https://api.moonshot.ai/anthropic"; + ANTHROPIC_AUTH_TOKEN = "sk-JXxvETDsMRTX7CP69dQZ34PTELQWVHAEk0PSxGDUh3OlnFWx"; packages = [gemini-cli]; buildInputs = nativeBuildInputs diff --git a/lib/router.ml b/lib/router.ml index 8649e5f..03e11f3 100644 --- a/lib/router.ml +++ b/lib/router.ml @@ -23,6 +23,20 @@ module R = Map.Make (struct ;; end) +let static path _db _req = + let pat = Parts.wildcard_match path in + match Body.sendfile pat with + | Error err -> Error err + | Ok body -> + (match Body.to_string body with + | Error e -> Error e + | Ok str -> Ok (Response.of_string ~body:str Status.(`Accepted))) +;; + +(* match Parts.wildcard_match path with *) +(* | "styles.css" -> Ok (Response.of_string ~body:"" Status.(`Accepted)) *) +(* | _ -> Ok (Response.of_string ~body:"" Status.(`Accepted)) *) + (* Define all routes in the application *) let routes = (* Use fold_left to build up a map of routes *) @@ -38,6 +52,7 @@ let routes = (* nil - end of path (no more segments) *) (* @--> - binds the route pattern to the handler function *) (* Handler.get_posts - the function that handles this route *) + (* ; `GET, s "assets" wildcard @--> static *) ; `GET, (s "posts" / int /? nil) @--> Handler.get_post (* / int - captures an integer parameter (post ID) *) ; `GET, (s "comments" / int /? nil) @--> Handler.get_comment -- cgit v1.2.3