Add seal site partial
parent
ccd0e31805
commit
1eb14f0e3d
|
@ -0,0 +1 @@
|
|||
use flake
|
|
@ -0,0 +1,3 @@
|
|||
[user]
|
||||
email = admin@cutesealfanpage.love
|
||||
name = Alice
|
|
@ -0,0 +1,15 @@
|
|||
# Haykll site
|
||||
_cache
|
||||
_site
|
||||
result
|
||||
|
||||
# Haskell stuff
|
||||
site
|
||||
dist
|
||||
*.dyn_hi
|
||||
*.dyn_o
|
||||
*.hi
|
||||
*.o
|
||||
|
||||
# Direnv lorri stuff
|
||||
.direnv
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"git.ignoreLimitWarning": true,
|
||||
"cSpell.words": [
|
||||
"cutesealfanpage",
|
||||
"Dhall",
|
||||
"Fanpage",
|
||||
"fmap",
|
||||
"Hakyll",
|
||||
"Lookie",
|
||||
"mappend",
|
||||
"Monoid",
|
||||
"Namecheap",
|
||||
"NOINLINE",
|
||||
"pandoc",
|
||||
"runghc",
|
||||
"uncurry",
|
||||
"unlines",
|
||||
"utct"
|
||||
],
|
||||
"nix.serverPath": "nil"
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
# Cute Seal Fanpage
|
||||
|
||||
> An experiment using [Nix](https://nixos.org/), [Hakyll](https://jaspervdj.be/hakyll/), [Haskell](https://www.haskell.org/), and [shell scripts](https://en.wikipedia.org/wiki/Bourne_shell) to automate a simple website deployment pipeline.
|
||||
|
||||
## Why Seals?
|
||||
|
||||
It's an in-joke.
|
||||
|
||||
## What's all this code?
|
||||
|
||||
seal-blog/
|
||||
|
||||
- devops/
|
||||
- build.sh
|
||||
- Uses a [nix-shell](https://nixos.wiki/wiki/Development_environment_with_nix-shell) expression to build the Hakyll executable
|
||||
|
||||
- configuration.nix
|
||||
- The [configuration file for the NixOS](https://nixos.org/manual/nixos/stable/index.html#ch-configuration) production server. This takes care of installing all the necessary software, setup [Nginx](https://www.nginx.com/), and get [ACME certs](https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment) for verification.
|
||||
|
||||
- newSealPost.sh
|
||||
- Script to be called daily by a cron job on the server. Generates the post for the day, builds and commits.
|
||||
|
||||
- website/
|
||||
- A basic Hakyll site, slightly modified to serve seals. Most of the site is generated from the `site.hs` file. Check the [Hakyll](https://jaspervdj.be/hakyll/) documentation for more info.
|
||||
|
||||
## Work to be done
|
||||
|
||||
- The blog post generation and the hosting of the website are currently intertwined when they should be separated
|
||||
- Seal post generator just makes posts
|
||||
- Hakyll blog imports or calls the post generator
|
||||
- The deployed server/nix config file has a cron job for adding a new blog and committing every day
|
||||
- Need to move the blog and post generation inside nixos-apps on my beefier server
|
||||
- The configuration file here is for it's own Linode, the current small one running, but I have a better setup for that now
|
||||
- Pull out the useful parts for my deployed server, remove anything not necessary for a small config file
|
||||
- Also switch to using caddy if not already
|
||||
- Experiment with `*` A records
|
||||
- For the seal blog
|
||||
- Instead of having `www` and `git` and `...` subdomains spelled out in Namecheap
|
||||
- Just have a `*` record and have caddy do the filtering
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: About Me
|
||||
---
|
||||
I collect the seal photos
|
||||
|
||||
Haven't missed a day yet!
|
|
@ -0,0 +1,102 @@
|
|||
{ sealImagesPath = "images"
|
||||
, postsOutputPath = "posts"
|
||||
, startDate = 2022-04-20
|
||||
, seed = +137
|
||||
, adjectives1 =
|
||||
[ "absorbing"
|
||||
, "adorable"
|
||||
, "alluring"
|
||||
, "ambrosial"
|
||||
, "amiable"
|
||||
, "appealing"
|
||||
, "attractive"
|
||||
, "beautiful"
|
||||
, "bewitching"
|
||||
, "captivating"
|
||||
, "charismatic"
|
||||
, "charming"
|
||||
, "choice"
|
||||
, "cute"
|
||||
, "dainty"
|
||||
, "darling"
|
||||
, "dear"
|
||||
, "delectable"
|
||||
, "delicate"
|
||||
, "delicious"
|
||||
, "delightful"
|
||||
, "desirable"
|
||||
, "dishy"
|
||||
, "dreamy"
|
||||
, "electrifying"
|
||||
, "elegant"
|
||||
, "enamoring"
|
||||
, "engaging"
|
||||
, "engrossing"
|
||||
, "enthralling"
|
||||
, "entrancing"
|
||||
, "eye-catching"
|
||||
, "fascinating"
|
||||
, "fetching"
|
||||
, "glamorous"
|
||||
, "graceful"
|
||||
, "heavenly"
|
||||
, "infatuating"
|
||||
, "inviting"
|
||||
, "irresistible"
|
||||
, "likable"
|
||||
, "lovable"
|
||||
, "lovely"
|
||||
, "magnetizing"
|
||||
, "nice"
|
||||
, "pleasant"
|
||||
, "precious"
|
||||
, "pretty"
|
||||
, "provocative"
|
||||
, "rapturous"
|
||||
, "ravishing"
|
||||
, "seducing"
|
||||
, "seductive"
|
||||
, "suave"
|
||||
, "sweet"
|
||||
, "tantalizing"
|
||||
, "tempting"
|
||||
, "titillating"
|
||||
, "winning"
|
||||
, "winsome"
|
||||
]
|
||||
, adjectives2 =
|
||||
[ "ample"
|
||||
, "bearish"
|
||||
, "big"
|
||||
, "butterball"
|
||||
, "buxom"
|
||||
, "chunky"
|
||||
, "fatty"
|
||||
, "flabby"
|
||||
, "fleshy"
|
||||
, "full-figured"
|
||||
, "hefty"
|
||||
, "husky"
|
||||
, "pleasingly plump"
|
||||
, "plump"
|
||||
, "plumpish"
|
||||
, "podgy"
|
||||
, "portly"
|
||||
, "pudgy"
|
||||
, "roly-poly"
|
||||
, "rotund"
|
||||
, "round"
|
||||
, "stout"
|
||||
, "tubby"
|
||||
, "zaftig"
|
||||
]
|
||||
, looks =
|
||||
[ "Look at"
|
||||
, "Gaze upon"
|
||||
, "Check out"
|
||||
, "Witness!"
|
||||
, "Look upon and tremble at"
|
||||
, "Lookie here at"
|
||||
, "Whoa! See"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Contact
|
||||
---
|
||||
If you have a seal or seal photos, I'd love to post them!
|
||||
|
||||
Send yours to admin AT cutesealfanpage.love
|
|
@ -0,0 +1,141 @@
|
|||
html {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 1.6rem;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 0.2rem solid #000;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 3rem;
|
||||
padding: 1.2rem 0;
|
||||
border-top: 0.2rem solid #000;
|
||||
font-size: 1.2rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
article .header {
|
||||
font-size: 1.4rem;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.logo a {
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (max-width: 319px) {
|
||||
body {
|
||||
width: 90%;
|
||||
margin: 0;
|
||||
padding: 0 5%;
|
||||
}
|
||||
header {
|
||||
margin: 4.2rem 0;
|
||||
}
|
||||
nav {
|
||||
margin: 0 auto 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
margin: 1rem auto 3rem;
|
||||
}
|
||||
.logo a {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
nav a {
|
||||
display: block;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 320px) {
|
||||
body {
|
||||
width: 90%;
|
||||
margin: 0;
|
||||
padding: 0 5%;
|
||||
}
|
||||
header {
|
||||
margin: 4.2rem 0;
|
||||
}
|
||||
nav {
|
||||
margin: 0 auto 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
margin: 1rem auto 3rem;
|
||||
}
|
||||
.logo a {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
nav a {
|
||||
display: inline;
|
||||
margin: 0 0.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
body {
|
||||
width: 60rem;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
header {
|
||||
margin: 0 0 3rem;
|
||||
padding: 1.2rem 0;
|
||||
}
|
||||
nav {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
}
|
||||
nav a {
|
||||
margin: 0 0 0 1.2rem;
|
||||
display: inline;
|
||||
}
|
||||
footer {
|
||||
text-align: right;
|
||||
}
|
||||
.logo {
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.logo a {
|
||||
float: left;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1691421349,
|
||||
"narHash": "sha256-RRJyX0CUrs4uW4gMhd/X4rcDG8PTgaaCQM5rXEJOx6g=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "011567f35433879aae5024fc6ec53f2a0568a6c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"spg": "spg",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"spg": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"utils": [
|
||||
"utils"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1691530010,
|
||||
"narHash": "sha256-4xtMxEstoQ0iM/BOgGhla08xL/DFjS0+Q8nLOl9Gmls=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "f25dd9c99382fd4e945e4d0d034254fc552d9aca",
|
||||
"revCount": 14,
|
||||
"type": "git",
|
||||
"url": "https://git.ewanick.com/bill/sealPostGenerator.git"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.ewanick.com/bill/sealPostGenerator.git"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1689068808,
|
||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
description = "A Nix flake for my long-running seal blog";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
|
||||
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
|
||||
spg.url = "git+https://git.ewanick.com/bill/sealPostGenerator.git";
|
||||
spg.inputs.nixpkgs.follows = "nixpkgs";
|
||||
spg.inputs.utils.follows = "utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, ... }@inputs:
|
||||
utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
ghc' = pkgs.haskellPackages.ghcWithHoogle (self: with self; [
|
||||
hakyll
|
||||
]);
|
||||
in
|
||||
rec {
|
||||
apps.generateSealPosts = {
|
||||
type = "app";
|
||||
program = "${inputs.spg.packages.${system}.default}/bin/generateSealPosts";
|
||||
};
|
||||
|
||||
apps.hakyll-site = {
|
||||
type = "app";
|
||||
program = "${packages.default}/bin/site";
|
||||
};
|
||||
|
||||
apps.default = apps.hakyll-site;
|
||||
|
||||
packages.default = pkgs.stdenv.mkDerivation {
|
||||
name = "site";
|
||||
src = self;
|
||||
buildPhase = ''
|
||||
${ghc'}/bin/ghc \
|
||||
-O2 \
|
||||
-static \
|
||||
-o site \
|
||||
site.hs
|
||||
'';
|
||||
installPhase = "mkdir -p $out/bin; install -t $out/bin site";
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
name = "hakyll-shell";
|
||||
|
||||
buildInputs = with pkgs.haskellPackages; [
|
||||
ghc'
|
||||
hlint
|
||||
haskell-language-server
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: Seal Blog!
|
||||
---
|
||||
|
||||
<h2>Welcome</h2>
|
||||
|
||||
<img src="/images/seal1.jpg" style="float: right; margin: 10px;" width="200px"/>
|
||||
|
||||
<p>Welcome to my seal blog!</p>
|
||||
|
||||
<p>Here's a list of recent posts for your reading pleasure:</p>
|
||||
|
||||
<h2>Seal Posts</h2>
|
||||
$partial("templates/post-list.html")$
|
||||
|
||||
<p>…or you can find more in the <a href="/archive.html">archives</a>.</p>
|
|
@ -0,0 +1,68 @@
|
|||
--------------------------------------------------------------------------------
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
import Data.Monoid (mappend)
|
||||
import Hakyll
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
main :: IO ()
|
||||
main = hakyll $ do
|
||||
match "images/*" $ do
|
||||
route idRoute
|
||||
compile copyFileCompiler
|
||||
|
||||
match "css/*" $ do
|
||||
route idRoute
|
||||
compile compressCssCompiler
|
||||
|
||||
match (fromList ["about.rst", "contact.markdown"]) $ do
|
||||
route $ setExtension "html"
|
||||
compile $ pandocCompiler
|
||||
>>= loadAndApplyTemplate "templates/default.html" defaultContext
|
||||
>>= relativizeUrls
|
||||
|
||||
match "posts/*" $ do
|
||||
route $ setExtension "html"
|
||||
compile $ pandocCompiler
|
||||
>>= loadAndApplyTemplate "templates/post.html" postCtx
|
||||
>>= loadAndApplyTemplate "templates/default.html" postCtx
|
||||
>>= relativizeUrls
|
||||
|
||||
create ["archive.html"] $ do
|
||||
route idRoute
|
||||
compile $ do
|
||||
posts <- recentFirst =<< loadAll "posts/*"
|
||||
let archiveCtx =
|
||||
listField "posts" postCtx (return posts) `mappend`
|
||||
constField "title" "Archives" `mappend`
|
||||
defaultContext
|
||||
|
||||
makeItem ""
|
||||
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
|
||||
>>= loadAndApplyTemplate "templates/default.html" archiveCtx
|
||||
>>= relativizeUrls
|
||||
|
||||
|
||||
match "index.html" $ do
|
||||
route idRoute
|
||||
compile $ do
|
||||
posts <- fmap (take 20) . recentFirst =<< loadAll "posts/*"
|
||||
-- posts <- recentFirst =<< loadAll "posts/*"
|
||||
let indexCtx =
|
||||
listField "posts" postCtx (return posts) `mappend`
|
||||
constField "title" "Home" `mappend`
|
||||
defaultContext
|
||||
|
||||
getResourceBody
|
||||
>>= applyAsTemplate indexCtx
|
||||
>>= loadAndApplyTemplate "templates/default.html" indexCtx
|
||||
>>= relativizeUrls
|
||||
|
||||
match "templates/*" $ compile templateBodyCompiler
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
postCtx :: Context String
|
||||
postCtx =
|
||||
dateField "date" "%B %e, %Y" `mappend`
|
||||
defaultContext
|
|
@ -0,0 +1,2 @@
|
|||
Here you can find all my previous posts:
|
||||
$partial("templates/post-list.html")$
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>My Seal Blog - $title$</title>
|
||||
<link rel="stylesheet" href="/css/default.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="logo">
|
||||
<a href="/">My Seal Blog</a>
|
||||
</div>
|
||||
<nav>
|
||||
<a href="/">Home</a>
|
||||
<a href="/about.html">About</a>
|
||||
<a href="/contact.html">Contact</a>
|
||||
<a href="/archive.html">Archive</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main role="main">
|
||||
<h1>$title$</h1>
|
||||
$body$
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
Site proudly generated by
|
||||
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
<ul>
|
||||
$for(posts)$
|
||||
<li>
|
||||
<a href="$url$">$title$</a> - $date$
|
||||
</li>
|
||||
$endfor$
|
||||
</ul>
|
|
@ -0,0 +1,11 @@
|
|||
<article>
|
||||
<section class="header">
|
||||
Posted on $date$
|
||||
$if(author)$
|
||||
by $author$
|
||||
$endif$
|
||||
</section>
|
||||
<section>
|
||||
$body$
|
||||
</section>
|
||||
</article>
|
|
@ -0,0 +1,11 @@
|
|||
name: website
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
cabal-version: >= 1.10
|
||||
|
||||
executable site
|
||||
main-is: site.hs
|
||||
build-depends: base == 4.*
|
||||
, hakyll == 4.13.*
|
||||
ghc-options: -threaded
|
||||
default-language: Haskell2010
|
Loading…
Reference in New Issue