summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-07-16 15:36:16 +0700
committerpolwex <polwex@sortug.com>2025-07-16 15:36:16 +0700
commit7acccc847e58ba5c04561890783bcd852df7e40b (patch)
tree896be9364df38fb4e4e96f08354f882ef947e69a
parent5bd56c54095a772cf14aae82389bf104338d7ac8 (diff)
m
-rw-r--r--CLAUDE.md91
-rw-r--r--dune2
-rw-r--r--flake.nix7
-rw-r--r--lib/router.ml15
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