now in ocaml
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
_esy/
|
||||
_build/
|
||||
node_modules
|
||||
esy.lock/
|
||||
*.db
|
||||
*.pem
|
||||
*~
|
||||
posts/*.post
|
||||
1
db/#inserts.ml#
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
95
db/database.ml
Normal file
@@ -0,0 +1,95 @@
|
||||
open Lwt
|
||||
|
||||
module BlogPost = struct
|
||||
type t = {
|
||||
slug : string;
|
||||
title : string;
|
||||
content : string;
|
||||
date : string;
|
||||
}
|
||||
end
|
||||
|
||||
module Q = struct
|
||||
open Caqti_request.Infix
|
||||
open Caqti_type.Std
|
||||
|
||||
let blog_post =
|
||||
let open BlogPost in
|
||||
let encode {slug; title; content; date} = Ok (slug, title, content, date) in
|
||||
let decode (slug, title, content, date) = Ok {slug; title; content; date} in
|
||||
let b_rep = Caqti_type.(tup4 string string string string) in
|
||||
custom ~encode ~decode b_rep
|
||||
|
||||
let create_blog_post_table =
|
||||
unit ->. unit @@
|
||||
{eos|
|
||||
CREATE TABLE IF NOT EXISTS blog_post (
|
||||
slug text PRIMARY KEY,
|
||||
title text NOT NULL,
|
||||
content text NOT NULL,
|
||||
date text NOT NULL,
|
||||
active TINYINT DEFAULT 1)
|
||||
|eos}
|
||||
|
||||
let create_blog_post =
|
||||
tup4 string string string string ->. unit @@
|
||||
"INSERT INTO blog_post (slug, title, content, date) VALUES (?, ?, ?, ?)"
|
||||
|
||||
let get_all_blog_posts =
|
||||
unit ->* blog_post @@
|
||||
"SELECT slug, title, content, date FROM blog_post WHERE active = 1"
|
||||
|
||||
let get_all_blog_posts_for_display =
|
||||
unit ->* tup2 string string @@
|
||||
"SELECT slug, title FROM blog_post WHERE active = 1"
|
||||
|
||||
let get_blog_post_by_slug =
|
||||
string ->? blog_post @@
|
||||
"SELECT slug, title, content, date FROM blog_post WHERE slug = ? AND active = 1"
|
||||
|
||||
let update_blog_post_content =
|
||||
tup2 string string ->. unit @@
|
||||
"UPDATE blog_post SET content = ? WHERE slug = ?"
|
||||
|
||||
let update_blog_post_title =
|
||||
tup2 string string ->. unit @@
|
||||
"UPDATE blog_post SET title = ? WHERE slug = ?"
|
||||
|
||||
let delete_blog_post =
|
||||
string ->. unit @@
|
||||
"UPDATE blog_post SET active = 0 WHERE slug = ?"
|
||||
end
|
||||
|
||||
module Db = (val Caqti_lwt.connect (Uri.of_string "sqlite3:database.db") >>= Caqti_lwt.or_fail |> Lwt_main.run)
|
||||
|
||||
(* Wrappers for the generic functions defined in Q *)
|
||||
module Database = struct
|
||||
let create_blog_post_table () =
|
||||
Db.exec Q.create_blog_post_table ()
|
||||
|
||||
let create_blog_post slug title content date =
|
||||
Db.exec Q.create_blog_post (slug, title, content, date)
|
||||
|
||||
let update_blog_post_content slug content =
|
||||
Db.exec Q.update_blog_post_content (content, slug)
|
||||
|
||||
let update_blog_post_title slug title =
|
||||
Db.exec Q.update_blog_post_title (title, slug)
|
||||
|
||||
let get_blog_post_by_slug slug =
|
||||
Db.find_opt Q.get_blog_post_by_slug slug
|
||||
|
||||
let get_all_blog_posts () =
|
||||
Db.collect_list Q.get_all_blog_posts ()
|
||||
|
||||
let iter_blog_posts f =
|
||||
Db.iter_s Q.get_all_blog_posts f ()
|
||||
|
||||
let (>>=?) monad func =
|
||||
monad >>= (function | Ok x -> func x | Error err -> Lwt.return (Error err))
|
||||
|
||||
let report_error = function
|
||||
| Ok () -> Lwt.return_unit
|
||||
| Error err ->
|
||||
Lwt_io.eprintl (Caqti_error.show err)
|
||||
end
|
||||
4
db/dune
Normal file
@@ -0,0 +1,4 @@
|
||||
(library
|
||||
(name database)
|
||||
(public_name rawleydotxyz.database)
|
||||
(libraries caqti caqti-lwt caqti-driver-sqlite3 lwt))
|
||||
3
db/inserts.ml
Normal file
@@ -0,0 +1,3 @@
|
||||
module Inserts = struct
|
||||
|
||||
end
|
||||
14
dune
Normal file
@@ -0,0 +1,14 @@
|
||||
(executable
|
||||
(name rawleydotxyz)
|
||||
(libraries dream caqti render database caqti-lwt caqti-driver-sqlite3 lwt))
|
||||
|
||||
(env
|
||||
(dev
|
||||
(flags (:standard -w -32))))
|
||||
|
||||
(rule
|
||||
(targets template.ml)
|
||||
(deps template.eml.ml)
|
||||
(action (run dream_eml %{deps} --workspace %{workspace_root})))
|
||||
|
||||
(data_only_dirs _esy esy.lock lib node_modules)
|
||||
23
dune-project
Normal file
@@ -0,0 +1,23 @@
|
||||
(lang dune 3.4)
|
||||
|
||||
(name rawleydotxyz)
|
||||
(generate_opam_files true)
|
||||
|
||||
(source
|
||||
(github rawleyfowler/rawleydotxyz))
|
||||
(authors "Rawley Fowler")
|
||||
(maintainers "Rawley Fowler")
|
||||
(license "AGPL3.0")
|
||||
|
||||
(package
|
||||
(name rawleydotxyz)
|
||||
(synopsis "Rawley Fowler's website + blog")
|
||||
(description "Rawley Fowler's personal website and blog, www.rawley.xyz")
|
||||
(allow_empty)
|
||||
(depends
|
||||
ocaml
|
||||
dune
|
||||
lwt
|
||||
dream
|
||||
caqti
|
||||
caqti-driver-sqlite))
|
||||
15
esy.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@opam/dream": "*",
|
||||
"@opam/dune": "*",
|
||||
"@opam/lwt": "*",
|
||||
"@opam/caqti": "*",
|
||||
"@opam/lwt_ppx": "*",
|
||||
"@opam/caqti-driver-sqlite3": "*",
|
||||
"ocaml": "4.12.x"
|
||||
},
|
||||
|
||||
"scripts": {
|
||||
"start": "dune exec --root . ./rawleydotxyz.exe"
|
||||
}
|
||||
}
|
||||
60
html/index.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Rawley.xyz, Rawley Fowler's blog and personal website">
|
||||
<title>rawley.xyz</title>
|
||||
<link href="/static/index.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>
|
||||
<a href="/">/home/rawley.xyz</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<li>
|
||||
<a href="/blog">blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/resume">resume</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/philosophy">philosophy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/web-ring">web ring</a>
|
||||
</li>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<h3>Welcome!</h3>
|
||||
<p>
|
||||
Hi, I'm Rawley. I'm a software developer from Saskatchewan, Canada.
|
||||
I am experienced with a wide variety of technologies, including
|
||||
but not limited to OCaml, Clojure, Scheme, GNU/Linux, OpenBSD, C/C++,
|
||||
and Java. I am particularily interested in functional programming and
|
||||
programming language theory.
|
||||
</p>
|
||||
<p>
|
||||
All of my personal projects are <a href="https://www.gnu.org/philosophy/philosophy.html">Free Software</a> and can be
|
||||
found on <a href="https://git.rawley.xyz">my git server</a> or, on my <a href="https://github.com/rawleyfowler">github</a>.
|
||||
I spend most of my free time working on personal projects,
|
||||
writing blog posts, or listening to bluegrass music.
|
||||
</p>
|
||||
<p>
|
||||
If you need to contact me, please send me an email.
|
||||
</p>
|
||||
</main>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>
|
||||
Copyright © Rawley Fowler 2022
|
||||
</p>
|
||||
<p>
|
||||
<b>Disclaimer</b>: All opinions on this site, are that of my own. They do not
|
||||
reflect the opinions of any of my employers; past, present, or future.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
63
html/philosophy.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Rawley.xyz, Rawley Fowler's software fundamentalist philosophy">
|
||||
<title>rawley.xyz/philosophy</title>
|
||||
<link href="/static/index.css" rel="stylesheet">
|
||||
<style>
|
||||
h4 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
i:before, i:after {
|
||||
content: "`";
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>
|
||||
<a href="/">/home/rawley.xyz</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<li>
|
||||
<a href="/blog">blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/resume">resume</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/philosophy">philosophy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/web-ring">web ring</a>
|
||||
</li>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<h3>Philosophy</h3>
|
||||
<p>
|
||||
Post early 2000's software is bloated, rights infringing, centralized and political.
|
||||
Long gone are the days of individual software freedom, and individual invention.
|
||||
The world has changed, and the world has fallen victim to the one and only thing that
|
||||
could possibly destroy freedom. Corporatist consumerism.
|
||||
</p>
|
||||
</main>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>
|
||||
Copyright © Rawley Fowler 2022
|
||||
</p>
|
||||
<p>
|
||||
<b>Disclaimer</b>: All opinions on this site, are that of my own. They do not
|
||||
reflect the opinions of any of my employers; past, present, or future.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
127
html/resume.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Rawley.xyz, Rawley Fowler's blog and personal website">
|
||||
<title>rawley.xyz/resume</title>
|
||||
<link href="/static/index.css" rel="stylesheet">
|
||||
<style>
|
||||
h4 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
i:before, i:after {
|
||||
content: "`";
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>
|
||||
<a href="/">/home/rawley.xyz</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<li>
|
||||
<a href="/blog">blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/resume">resume</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/philosophy">philosophy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/web-ring">web ring</a>
|
||||
</li>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<h3>Rawley Fowler ~ Software Developer</h3>
|
||||
<p>
|
||||
I am an experienced and pragmatic software developer from
|
||||
Canada with a particular interest in functional programming,
|
||||
operating systems and programming language theory.
|
||||
</p>
|
||||
<h4>Skills</h4>
|
||||
<h5>Operating Systems</h5>
|
||||
<p>
|
||||
<i>Debian Linux</i>
|
||||
<i>Fedora Linux</i>
|
||||
<i>OpenBSD</i>
|
||||
<i>Void Linux</i>
|
||||
<i>FreeBSD</i>
|
||||
</p>
|
||||
<h5>Programming Languages</h5>
|
||||
<p>
|
||||
<i>OCaml</i>
|
||||
<i>Scheme</i>
|
||||
<i>Clojure</i>
|
||||
<i>Go</i>
|
||||
<i>Bash</i>
|
||||
<i>ELisp</i>
|
||||
<i>C/C++</i>
|
||||
<i>Perl</i>
|
||||
<i>Ruby</i>
|
||||
<i>JavaScript</i>
|
||||
<i>TypeScript</i>
|
||||
<i>Java</i>
|
||||
</p>
|
||||
<h5>Misc</h5>
|
||||
<p>
|
||||
<i>Git</i>
|
||||
<i>CMake</i>
|
||||
<i>Dune</i>
|
||||
<i>Esy</i>
|
||||
<i>OracleSQL</i>
|
||||
<i>Docker</i>
|
||||
<i>Docker-Compose</i>
|
||||
<i>PostgreSQL</i>
|
||||
<i>SQLite3</i>
|
||||
<i>MySQL</i>
|
||||
</p>
|
||||
<h4>Experience</h4>
|
||||
<b>DevPro Consulting</b>: Fullstack Developer Consultant (2022 - Present)
|
||||
<p>
|
||||
Full stack microservice web development with Java,
|
||||
Spring Boot, Web Sockets, Angular and SCSS.
|
||||
</p>
|
||||
<h4>Projects & Contributions</h4>
|
||||
<b><a href="https://github.com/rawleyfowler/sluj">Sluj</a></b>:
|
||||
The small and efficient URI slugging library for Clojure.
|
||||
<p>
|
||||
Sluj is the only 100% Clojure slugging solution for the JVM. It's goal
|
||||
is to be as simple, and efficient as possible.
|
||||
</p>
|
||||
<b><a href="https://github.com/rawleyfowler/rawleydotxyz">This Website</a></b>:
|
||||
My website and blog written in OCaml.
|
||||
<p>
|
||||
My website, <a href="/">rawley.xyz</a> is written in OCaml Dream,
|
||||
and interfaces with a SQLite3 database for my blog using Caqti. My website
|
||||
is designed to be fairly simple (and used to be written in Go). Right now,
|
||||
it's hosted on a Debian 11 server and served via NGiNX and OCaml.
|
||||
</p>
|
||||
<b><a href="https://github.com/noqqe/why-openbsd.rocks">why-openbsd.rocks</a></b>
|
||||
A website for getting random facts about OpenBSD.
|
||||
<p>
|
||||
Reworked/updated the JavaScript used on <a href="https://why-openbsd.rocks">why-openbsd.rocks</a>.
|
||||
Previously the code was using XMLHttpRequests directly,
|
||||
I refactored it to use the newer, more standard fetch API.
|
||||
</p>
|
||||
</main>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>
|
||||
Copyright © Rawley Fowler 2022
|
||||
</p>
|
||||
<p>
|
||||
<b>Disclaimer</b>: All opinions on this site, are that of my own. They do not
|
||||
reflect the opinions of any of my employers; past, present, or future.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
79
html/web-ring.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Rawley.xyz, Rawley Fowler's web ring">
|
||||
<title>rawley.xyz/web-ring</title>
|
||||
<link href="/static/index.css" rel="stylesheet">
|
||||
<style>
|
||||
main a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>
|
||||
<a href="/">/home/rawley.xyz</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<li>
|
||||
<a href="/blog">blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/resume">resume</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/philosophy">philosophy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/web-ring">web ring</a>
|
||||
</li>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<h3>Web Ring</h3>
|
||||
<p>
|
||||
These are sites I like, sites that my friends host, or sites that I think are
|
||||
useful in some way.
|
||||
</p>
|
||||
<div>
|
||||
<a href="https://based.cooking">
|
||||
<img src="/static/basedcooking.gif" alt="based.cooking" />
|
||||
</a>
|
||||
<a href="/">
|
||||
<img src="/static/rawley.xyz.png" alt="rawley.xyz" />
|
||||
</a>
|
||||
<a href="https://tomfasano.net">
|
||||
<img src="/static/tomfasano.gif" alt="tomfasano.net" />
|
||||
</a>
|
||||
<a href="https://www.getmonero.org">
|
||||
<img src="/static/getmonero.gif" alt="monero" />
|
||||
</a>
|
||||
<a>
|
||||
<img src="/static/banporn.gif" alt="ban porn" />
|
||||
</a>
|
||||
<a href="https://www.debian.org">
|
||||
<img src="/static/debian.gif" alt="debian linux" />
|
||||
</a>
|
||||
<a href="https://dfitw.xyz">
|
||||
<img src="/static/dfitw.xyz.png" alt="dfitw.xyz" />
|
||||
</a>
|
||||
<a href="https://www.gnu.org/software/emacs">
|
||||
<img src="/static/emacs.png" alt="GNU Emacs" />
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>
|
||||
Copyright © Rawley Fowler 2022
|
||||
</p>
|
||||
<p>
|
||||
<b>Disclaimer</b>: All opinions on this site, are that of my own. They do not
|
||||
reflect the opinions of any of my employers; past, present, or future.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
66
insert-posts.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
function usage() {
|
||||
echo "Usage: insert-posts"
|
||||
echo "Tool for inserting posts into a SQLite3 database."
|
||||
echo 'Posts should be located in $BLOG_DIR/posts, and the database should be in $BLOG_DIR'
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -x "$(command -v sqlite3)" ] && echo "You need SQLite3 to run this" && exit 1
|
||||
[ -z "$BLOG_DIR" ] && echo '$BLOG_DIR is not set' && usage
|
||||
|
||||
# params -> slug, title, content, date
|
||||
function update_post() {
|
||||
local date=${1:?Date is required}
|
||||
local slug=${2:?Slug is required}
|
||||
local title=${3:?Title is required}
|
||||
local content=${4:?Content is required}
|
||||
|
||||
sqlite3 "$BLOG_DIR/database.db" "UPDATE blog_post SET title = '$title', content = '$content' WHERE slug = '$slug'" 2&> /dev/null
|
||||
|
||||
local status=$?
|
||||
|
||||
echo $status
|
||||
|
||||
[ $status -eq 0 ] && echo "Updated already existing post: $slug."
|
||||
[ $status -eq 1 ] && echo "Failed to update, or insert post: $slug."
|
||||
}
|
||||
|
||||
# params -> slug, title, content, date
|
||||
function insert_post() {
|
||||
[ $# -eq 0 ] && exit 1;
|
||||
|
||||
local date=${1:?Date is required}
|
||||
local slug=${2:?Slug is required}
|
||||
local title=${3:?Title is required}
|
||||
local content=${4:?Content is required}
|
||||
|
||||
sqlite3 "$BLOG_DIR/database.db" "INSERT INTO blog_post (slug, title, content, date) VALUES ('$slug', '$title', '$content', '$date');" 2&> /dev/null
|
||||
|
||||
local status=$?
|
||||
|
||||
echo $status
|
||||
|
||||
[ $status -eq 0 ] && echo "Inserted $slug successfully." && return
|
||||
[ $status -eq 19 ] && update_post "$date" "$slug" "$title" "$content"
|
||||
}
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# Blog post files are structured as follows: #
|
||||
# FIRST LINE -> date in desired format #
|
||||
# SECOND LINE -> URI friendly slug #
|
||||
# THIRD LINE -> Title of post #
|
||||
# REMAINING LINES -> Content of post #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
for file in $(ls "$BLOG_DIR/posts"); do
|
||||
echo "Trying to insert: $file"
|
||||
target_file="$BLOG_DIR/posts/$file"
|
||||
date=$(sed -n '1p' $target_file)
|
||||
slug=$(sed -n '2p' $target_file)
|
||||
title=$(sed -n '3p' $target_file)
|
||||
content=$(sed -n '4,$p' "$target_file")
|
||||
|
||||
insert_post "$date" "$slug" "$title" "$content"
|
||||
done
|
||||
0
posts/.gitkeep
Normal file
25
rawleydotxyz.ml
Normal file
@@ -0,0 +1,25 @@
|
||||
open Lwt.Infix
|
||||
open Database
|
||||
open Render
|
||||
|
||||
exception DatabaseCreationFailure
|
||||
|
||||
let _ =
|
||||
Database.create_blog_post_table () >>= function
|
||||
| Ok () -> Lwt_io.print "Database was initialized successfully\n"
|
||||
| Error _ -> raise DatabaseCreationFailure
|
||||
|
||||
let () =
|
||||
Dream.run
|
||||
@@ Dream.logger
|
||||
@@ Dream.router [
|
||||
Dream.get "/static/**" @@ Dream.static "static";
|
||||
Dream.get "/" @@ Dream.from_filesystem "html" "index.html";
|
||||
Dream.get "/resume" @@ Dream.from_filesystem "html" "resume.html";
|
||||
Dream.get "/philosophy" @@ Dream.from_filesystem "html" "philosophy.html";
|
||||
Dream.get "/web-ring" @@ Dream.from_filesystem "html" "web-ring.html";
|
||||
Dream.get "/blog" Render.render_blog_index;
|
||||
Dream.scope "/blog" [Dream.origin_referrer_check] [
|
||||
Dream.get "/:post" Render.render_blog_post
|
||||
];
|
||||
]
|
||||
4
render/dune
Normal file
@@ -0,0 +1,4 @@
|
||||
(library
|
||||
(name render)
|
||||
(public_name rawleydotxyz.render)
|
||||
(libraries dream lwt database))
|
||||
108
render/render.ml
Normal file
@@ -0,0 +1,108 @@
|
||||
open Database
|
||||
open Lwt
|
||||
|
||||
module Render = struct
|
||||
let not_found_template = "404 Not found"
|
||||
let error_template = "500 Server Error"
|
||||
|
||||
let header_template =
|
||||
{eos|
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Rawley.xyz, Rawley Fowler's blog and personal website">
|
||||
<title>rawley.xyz</title>
|
||||
<link href="/static/index.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<body>
|
||||
<header>
|
||||
<h1>
|
||||
<a href="/">/home/rawley.xyz</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<li>
|
||||
<a href="/blog">blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/resume">resume</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/philosophy">philosophy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/web-ring">web ring</a>
|
||||
</li>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
|eos}
|
||||
|
||||
let footer_template =
|
||||
{eos|
|
||||
</main>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>
|
||||
Copyright © Rawley Fowler 2022
|
||||
</p>
|
||||
<p>
|
||||
<b>Disclaimer</b>: All opinions on this site, are that of my own. They do not
|
||||
reflect the opinions of any of my employers; past, present, or future.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|eos}
|
||||
|
||||
let render_page content =
|
||||
Printf.sprintf
|
||||
{eos|
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
|eos}
|
||||
header_template
|
||||
content
|
||||
footer_template
|
||||
|
||||
let generate_link (p : BlogPost.t) =
|
||||
Printf.sprintf
|
||||
{eos|
|
||||
<div class="link-wrapper">
|
||||
<a href="/blog/%s">%s</a>
|
||||
<i>%s</i>
|
||||
</div>
|
||||
|eos}
|
||||
p.slug p.title p.date
|
||||
|
||||
let handle_error e =
|
||||
print_endline (Caqti_error.show e); error_template |> Dream.html
|
||||
|
||||
let render_blog_post request =
|
||||
let slug = Dream.param request "post" in
|
||||
let post_t = Database.get_blog_post_by_slug slug in
|
||||
post_t >>= fun post ->
|
||||
match post with
|
||||
| Error e -> handle_error e
|
||||
| Ok p_opt ->
|
||||
match p_opt with
|
||||
| None -> not_found_template |> Dream.html
|
||||
| Some p ->
|
||||
Printf.sprintf "<h2>%s</h2>\r\n%s" p.title p.content
|
||||
|> render_page
|
||||
|> Dream.html
|
||||
|
||||
let render_blog_index _ =
|
||||
let buff = Buffer.create 512 in
|
||||
let () = Buffer.add_string buff "<h3>Blog</h3>" in
|
||||
let posts_t = Database.get_all_blog_posts () in
|
||||
posts_t >>= function
|
||||
| Error e -> handle_error e
|
||||
| Ok posts ->
|
||||
List.iter (fun p -> Buffer.add_string buff (generate_link p)) posts;
|
||||
let c = if List.length posts <> 0 then
|
||||
render_page @@ Buffer.contents buff
|
||||
else render_page "<br>No blog posts..." in
|
||||
Dream.html c
|
||||
end
|
||||
BIN
static/1661749187152.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
static/banporn.gif
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
static/basedcooking.gif
Normal file
|
After Width: | Height: | Size: 932 B |
BIN
static/debian.gif
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
static/dfitw.xyz.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
static/emacs.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
static/getmonero.gif
Normal file
|
After Width: | Height: | Size: 23 KiB |
29
static/index.css
Normal file
@@ -0,0 +1,29 @@
|
||||
body {
|
||||
font-size: calc(14px + 0.11vw);
|
||||
width: clamp(373px, 45vw, 50vw);
|
||||
color: black;
|
||||
margin: auto;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
nav {
|
||||
font-size: calc(16px + 0.33vh);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
nav > * {
|
||||
margin-right: 33px;
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
body * {
|
||||
color: lightgrey;
|
||||
}
|
||||
}
|
||||
BIN
static/rawley.xyz.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/tomfasano.gif
Normal file
|
After Width: | Height: | Size: 5.0 KiB |