~jeffa/jeffa.io

ebe46064263b8c6a892c97afe041c4c19f16489a — Jeff a month ago 1adae71
Overhaul project
59 files changed, 421 insertions(+), 532 deletions(-)

M Dockerfile
A client/build.sh
R scripts/pandoc/article.yml => client/pandoc/article.yml
R scripts/pandoc/page.html => client/pandoc/page.html
R src/client/content/an_auto-increment_crate_for_rust.md => client/src/content/an_auto-increment_crate_for_rust.md
R src/client/content/hulu_location_cookie_spoof.md => client/src/content/hulu_location_cookie_spoof.md
R src/client/content/index.md => client/src/content/index.md
R src/client/content/recommended.md => client/src/content/recommended.md
R src/client/content/sidebar/sidebar.md => client/src/content/sidebar/sidebar.md
R src/client/images/hulu_location_services_request.png => client/src/images/hulu_location_services_request.png
R src/client/images/jeffa.ico => client/src/images/jeffa.ico
R src/client/stylesheets/code.css => client/src/stylesheets/code.css
R src/client/stylesheets/global.css => client/src/stylesheets/global.css
R src/client/stylesheets/index.css => client/src/stylesheets/index.css
R src/client/stylesheets/input.css => client/src/stylesheets/input.css
A client/src/stylesheets/iosevka-sparkle.css
R src/client/stylesheets/layout.css => client/src/stylesheets/layout.css
R src/client/stylesheets/lists.css => client/src/stylesheets/lists.css
R src/client/stylesheets/normalize.css => client/src/stylesheets/normalize.css
R src/client/stylesheets/spacing.css => client/src/stylesheets/spacing.css
R src/client/stylesheets/tables.css => client/src/stylesheets/tables.css
R src/client/stylesheets/typography.css => client/src/stylesheets/typography.css
A client/src/stylesheets/woff2/iosevka-sparkle-bold.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-bolditalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-boldoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-extrabold.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-extrabolditalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-extraboldoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-extralight.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-extralightitalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-extralightoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-heavy.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-heavyitalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-heavyoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-italic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-light.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-lightitalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-lightoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-medium.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-mediumitalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-mediumoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-oblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-regular.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-semibold.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-semibolditalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-semiboldoblique.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-thin.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-thinitalic.woff2
A client/src/stylesheets/woff2/iosevka-sparkle-thinoblique.woff2
A nginx.conf
D scripts/build-client.fish
D scripts/build.fish
D scripts/deploy.sh
D scripts/serve.sh
D src/client/stylesheets/iosevka-aile.css
D src/server/Cargo.lock
D src/server/Cargo.toml
D src/server/src/main.rs
D src/server/src/request.rs
M Dockerfile => Dockerfile +9 -19
@@ 4,35 4,25 @@ FROM node:buster AS build-client
RUN apt-get update && \
    apt-get upgrade --assume-yes

RUN npm install --global purgecss uglifycss && \
    curl -L -o /home/pandoc.deb https://github.com/jgm/pandoc/releases/download/2.11.4/pandoc-2.11.4-1-amd64.deb && \
    dpkg -i /home/pandoc.deb

WORKDIR /jeffa.io/

COPY . .

RUN sh scripts/build-client.fish
RUN npm install --global purgecss uglifycss

# Build server
FROM rust:buster AS build-server
RUN curl -L -o /home/pandoc.deb https://github.com/jgm/pandoc/releases/download/2.11.4/pandoc-2.11.4-1-amd64.deb && \
    dpkg -i /home/pandoc.deb

WORKDIR /jeffa.io/

COPY . .

RUN cd src/server && cargo build --release

RUN mkdir build && \
    cp src/server/target/release/server build/
RUN sh client/build.sh

# Run
FROM debian:buster-slim
FROM nginx:latest

WORKDIR /jeffa.io/

COPY --from=build-client /jeffa.io/build/* ./build/
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=build-client /jeffa.io/client/build/ /jeffa.io/

COPY --from=build-server /jeffa.io/build/* ./build/
EXPOSE 9876:9876

ENTRYPOINT build/server
CMD ["nginx", "-g", "daemon off;"]

A client/build.sh => client/build.sh +52 -0
@@ 0,0 1,52 @@
#! /bin/bash
#
# Run this script from the project root
#
# Step 1 - Output folders
# Create or empty build, client and client/tmp directories

if [ -e client/build/ ]
    then
    rm -r client/build
else
    mkdir client/build
fi

mkdir \
    client/build/ \
    client/build/tmp \
    client/build/woff2 \

# Step 2 - Markdown Conversion
# Compile Markdown to HTML

for file in client/src/content/*.md
do
    pandoc "$file" \
        -V nav="$(pandoc -t html5 client/src/content/sidebar/sidebar.md)" \
        --defaults client/pandoc/article.yml \
        -o "${file%.md}.html"
done

mv client/src/content/*.html client/build/ && \

# Step 3 - Assets
# Trim unused CSS and minify the output to a single file

purgecss \
    --css client/src/stylesheets/*.css \
    --content client/build/*.html \
    --output client/build/tmp && \

uglifycss client/build/tmp/*.css > client/build/index.css && \

# Copy assets to client/build

cp client/src/images/* client/build/ && \
    cp client/src/stylesheets/woff2/* client/build/woff2 && \
    mv client/build/jeffa.ico client/build/favicon.ico &&

# Step 4
# Clean up by removing client/build/tmp/

rm -r client/build/tmp

R scripts/pandoc/article.yml => client/pandoc/article.yml +1 -1
@@ 2,7 2,7 @@

from: markdown
to: html5
template: scripts/pandoc/page.html
template: client/pandoc/page.html
standalone: false # output fragments
self-contained: false # do not embed in HTML
metadata:

R scripts/pandoc/page.html => client/pandoc/page.html +0 -1
@@ 6,7 6,6 @@
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="author" content="Jeff Anderson" />
  <title>jeffa.io - ${title}</title>
  <link rel="preconnect" href="https://github.com/be5invis/Iosevka/releases/download/v4.5.0/webfont-iosevka-sparkle-4.5.0.zip">
  <link rel="stylesheet" href="index.css" />
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>

R src/client/content/an_auto-increment_crate_for_rust.md => client/src/content/an_auto-increment_crate_for_rust.md +0 -0
R src/client/content/hulu_location_cookie_spoof.md => client/src/content/hulu_location_cookie_spoof.md +0 -0
R src/client/content/index.md => client/src/content/index.md +0 -0
R src/client/content/recommended.md => client/src/content/recommended.md +0 -0
R src/client/content/sidebar/sidebar.md => client/src/content/sidebar/sidebar.md +0 -0
R src/client/images/hulu_location_services_request.png => client/src/images/hulu_location_services_request.png +0 -0
R src/client/images/jeffa.ico => client/src/images/jeffa.ico +0 -0
R src/client/stylesheets/code.css => client/src/stylesheets/code.css +0 -0
R src/client/stylesheets/global.css => client/src/stylesheets/global.css +9 -6
@@ 1,3 1,5 @@
@import "iosevka-sparkle.css";

/* NOTE html is set to 62.5% so that all the REM measurements throughout
Skeleton are based on 10px sizing. So basically 1.5rem = 15px :) */
html {


@@ 9,13 11,14 @@ body {
    /* currently ems cause chrome bug misinterpreting rems on body element */
    line-height: 1.6;
    font-weight: 500;
    font-family: "Iosevka Aile", "Raleway", "HelveticaNeue",
                 "Helvetica Neue", "Helvetica", "Arial", "sans-serif";
    color      : var(--fg);
    font-family: "Iosevka Sparkle", "Raleway", "HelveticaNeue",
        "Helvetica Neue", "Helvetica", "Arial", "sans-serif";
    color: var(--fg);
}

html, body {
    margin: 0;
html,
body {
    margin : 0;
    padding: 0;
    height: 100%;
    height : 100%;
}

R src/client/stylesheets/index.css => client/src/stylesheets/index.css +0 -0
R src/client/stylesheets/input.css => client/src/stylesheets/input.css +0 -0
A client/src/stylesheets/iosevka-sparkle.css => client/src/stylesheets/iosevka-sparkle.css +314 -0
@@ 0,0 1,314 @@
@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-thin.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-thinoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-thinoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-thinitalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-extralight.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-extralightoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-extralightoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-extralightitalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-light.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-lightoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-lightoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-lightitalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-regular.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-oblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-oblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-italic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-medium.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-mediumoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-mediumoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-mediumitalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-semibold.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-semiboldoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-semiboldoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-semibolditalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-bold.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-boldoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-boldoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-bolditalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-extrabold.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-extraboldoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-extraboldoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-extrabolditalic.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	font-style: normal;
	src: url('woff2/iosevka-sparkle-heavy.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	font-style: oblique;
	src: url('woff2/iosevka-sparkle-heavyoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle Oblique';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	src: url('woff2/iosevka-sparkle-heavyoblique.woff2') format('woff2');
}

@font-face {
	font-family: 'Iosevka Sparkle';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	font-style: italic;
	src: url('woff2/iosevka-sparkle-heavyitalic.woff2') format('woff2');
}

R src/client/stylesheets/layout.css => client/src/stylesheets/layout.css +0 -0
R src/client/stylesheets/lists.css => client/src/stylesheets/lists.css +0 -0
R src/client/stylesheets/normalize.css => client/src/stylesheets/normalize.css +0 -0
R src/client/stylesheets/spacing.css => client/src/stylesheets/spacing.css +0 -0
R src/client/stylesheets/tables.css => client/src/stylesheets/tables.css +0 -0
R src/client/stylesheets/typography.css => client/src/stylesheets/typography.css +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-bold.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-bold.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-bolditalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-bolditalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-boldoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-boldoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-extrabold.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-extrabold.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-extrabolditalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-extrabolditalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-extraboldoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-extraboldoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-extralight.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-extralight.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-extralightitalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-extralightitalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-extralightoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-extralightoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-heavy.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-heavy.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-heavyitalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-heavyitalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-heavyoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-heavyoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-italic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-italic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-light.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-light.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-lightitalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-lightitalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-lightoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-lightoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-medium.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-medium.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-mediumitalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-mediumitalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-mediumoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-mediumoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-oblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-oblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-regular.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-regular.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-semibold.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-semibold.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-semibolditalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-semibolditalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-semiboldoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-semiboldoblique.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-thin.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-thin.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-thinitalic.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-thinitalic.woff2 +0 -0
A client/src/stylesheets/woff2/iosevka-sparkle-thinoblique.woff2 => client/src/stylesheets/woff2/iosevka-sparkle-thinoblique.woff2 +0 -0
A nginx.conf => nginx.conf +36 -0
@@ 0,0 1,36 @@
http {
    include /etc/nginx/mime.types;
    keepalive_timeout 65;

    server {
        listen 9876;
        server_name  jeffa.io;

        gzip on;
        gzip_types text/css font/woff2 image/png;
        gzip_proxied no-cache no-store private expired auth;
        gzip_min_length 1000;

        root /jeffa.io/;
        index  index.html index.htm;

        location / {
            autoindex on;
            gzip_static on;
        }

        error_page  404              /404.txt;
        location /404.txt {
            add_header Content-Type text/plain;
            return 404;
        }

        error_page   500 502 503 504  /50x.txt;
        location /50x.txt {
            add_header Content-Type text/plain;
            return 500;
        }
    }
}

events {}

D scripts/build-client.fish => scripts/build-client.fish +0 -47
@@ 1,47 0,0 @@
# Step 1 - Output folders
# Create or empty build, client and client/tmp directories

if [ -e build/ ]
then
    rm -r build/*
else
    mkdir build
fi


mkdir build/client && \
mkdir build/client/tmp && \

# Step 2 - Markdown Conversion
# Compile Markdown to HTML

for file in src/client/content/*.md
do
    pandoc \
        "$file" \
        -V nav="$(pandoc -t html5 src/client/content/sidebar/sidebar.md)" \
        --defaults scripts/pandoc/article.yml \
        -o "${file%.md}.html"
done

mv src/client/content/*.html build/client/ && \

# Step 3 - Assets
# Trim unused CSS and minify the output to a single file

purgecss \
    --css src/client/stylesheets/*.css \
    --content build/client/*.html \
    --output build/client/tmp && \

uglifycss build/client/tmp/*.css > build/client/index.css && \

# Copy assets to build/client

cp src/client/images/* build/client/ && \
mv build/client/jeffa.ico build/client/favicon.ico &&

# Step 4
# Clean up by removing build/client/tmp/

rm -r build/client/tmp

D scripts/build.fish => scripts/build.fish +0 -4
@@ 1,4 0,0 @@
echo "Building client..." & sh scripts/build-client.fish
wait

echo "Building server image..." & docker build -t jeffa.io .

D scripts/deploy.sh => scripts/deploy.sh +0 -1
@@ 1,1 0,0 @@
surge public/ jeffa.io

D scripts/serve.sh => scripts/serve.sh +0 -1
@@ 1,1 0,0 @@
http-server -c-0 public/

D src/client/stylesheets/iosevka-aile.css => src/client/stylesheets/iosevka-aile.css +0 -315
@@ 1,315 0,0 @@

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-thin.woff2') format('woff2'), url('ttf/iosevka-aile-thin.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-thinoblique.woff2') format('woff2'), url('ttf/iosevka-aile-thinoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	src: url('iosevka-aile-thinoblique.woff2') format('woff2'), url('ttf/iosevka-aile-thinoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 100;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-thinitalic.woff2') format('woff2'), url('ttf/iosevka-aile-thinitalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-extralight.woff2') format('woff2'), url('ttf/iosevka-aile-extralight.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-extralightoblique.woff2') format('woff2'), url('ttf/iosevka-aile-extralightoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	src: url('iosevka-aile-extralightoblique.woff2') format('woff2'), url('ttf/iosevka-aile-extralightoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 200;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-extralightitalic.woff2') format('woff2'), url('ttf/iosevka-aile-extralightitalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-light.woff2') format('woff2'), url('ttf/iosevka-aile-light.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-lightoblique.woff2') format('woff2'), url('ttf/iosevka-aile-lightoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	src: url('iosevka-aile-lightoblique.woff2') format('woff2'), url('ttf/iosevka-aile-lightoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 300;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-lightitalic.woff2') format('woff2'), url('ttf/iosevka-aile-lightitalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-regular.woff2') format('woff2'), url('ttf/iosevka-aile-regular.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-oblique.woff2') format('woff2'), url('ttf/iosevka-aile-oblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	src: url('iosevka-aile-oblique.woff2') format('woff2'), url('ttf/iosevka-aile-oblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 400;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-italic.woff2') format('woff2'), url('ttf/iosevka-aile-italic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-medium.woff2') format('woff2'), url('ttf/iosevka-aile-medium.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-mediumoblique.woff2') format('woff2'), url('ttf/iosevka-aile-mediumoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	src: url('iosevka-aile-mediumoblique.woff2') format('woff2'), url('ttf/iosevka-aile-mediumoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 500;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-mediumitalic.woff2') format('woff2'), url('ttf/iosevka-aile-mediumitalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-semibold.woff2') format('woff2'), url('ttf/iosevka-aile-semibold.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-semiboldoblique.woff2') format('woff2'), url('ttf/iosevka-aile-semiboldoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	src: url('iosevka-aile-semiboldoblique.woff2') format('woff2'), url('ttf/iosevka-aile-semiboldoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 600;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-semibolditalic.woff2') format('woff2'), url('ttf/iosevka-aile-semibolditalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-bold.woff2') format('woff2'), url('ttf/iosevka-aile-bold.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-boldoblique.woff2') format('woff2'), url('ttf/iosevka-aile-boldoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	src: url('iosevka-aile-boldoblique.woff2') format('woff2'), url('ttf/iosevka-aile-boldoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 700;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-bolditalic.woff2') format('woff2'), url('ttf/iosevka-aile-bolditalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-extrabold.woff2') format('woff2'), url('ttf/iosevka-aile-extrabold.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-extraboldoblique.woff2') format('woff2'), url('ttf/iosevka-aile-extraboldoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	src: url('iosevka-aile-extraboldoblique.woff2') format('woff2'), url('ttf/iosevka-aile-extraboldoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 800;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-extrabolditalic.woff2') format('woff2'), url('ttf/iosevka-aile-extrabolditalic.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	font-style: normal;
	src: url('iosevka-aile-heavy.woff2') format('woff2'), url('ttf/iosevka-aile-heavy.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	font-style: oblique;
	src: url('iosevka-aile-heavyoblique.woff2') format('woff2'), url('ttf/iosevka-aile-heavyoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile Oblique';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	src: url('iosevka-aile-heavyoblique.woff2') format('woff2'), url('ttf/iosevka-aile-heavyoblique.ttf') format('truetype');
}

@font-face {
	font-family: 'Iosevka Aile';
	font-display: swap;
	font-weight: 900;
	font-stretch: normal;
	font-style: italic;
	src: url('iosevka-aile-heavyitalic.woff2') format('woff2'), url('ttf/iosevka-aile-heavyitalic.ttf') format('truetype');
}

D src/server/Cargo.lock => src/server/Cargo.lock +0 -5
@@ 1,5 0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "jeffaio-server"
version = "0.1.0"

D src/server/Cargo.toml => src/server/Cargo.toml +0 -6
@@ 1,6 0,0 @@
[package]
name = "jeffaio-server"
version = "0.1.0"
authors = ["Jeff <dev@jeffa.io>"]
edition = "2018"


D src/server/src/main.rs => src/server/src/main.rs +0 -48
@@ 1,48 0,0 @@
mod request;

use std::fs;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    let buffer = [0; 1024];

    for stream in listener.incoming() {
        let stream = stream.unwrap();

        handle_connection(buffer, stream);
    }
}

fn handle_connection(mut buffer: [u8; 1024], mut stream: TcpStream) {
    stream.read(&mut buffer).unwrap();

    let get = b"GET / HTTP/1.1\r\n";

    if buffer.starts_with(get) {
        let contents = fs::read_to_string("hello.html").unwrap();

        let response = format!(
            "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
            contents.len(),
            contents
        );

        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    } else {
        let status_line = "HTTP/1.1 404 NOT FOUND";
        let contents = fs::read_to_string("404.html").unwrap();

        let response = format!(
            "{}\r\nContent-Length: {}\r\n\r\n{}",
            status_line,
            contents.len(),
            contents
        );

        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }
}

D src/server/src/request.rs => src/server/src/request.rs +0 -78
@@ 1,78 0,0 @@
const GET: &'static str = "GET";
const POST: &'static str = "POST";

const HTTP_1_0: &'static str = "HTTP/1.0";
const HTTP_1_1: &'static str = "HTTP/1.1";

struct Request {
    method: &'static str,
    target: String,
    version: &'static str,
}

impl Request {
    pub fn new(raw: &str) -> Result<Self, String> {
        let mut lines = raw.split("\n");

        let start_line = {
            if let Some(line) = lines.next() {
                line
            } else {
                return Err("Could not read start line of HTTP request.".to_string());
            }
        };

        let mut start_line_parts = start_line.split(" ");

        let method = {
            let method_str = {
                if let Some(method_str) = start_line_parts.next() {
                    method_str
                } else {
                    return Err("Could not read method of HTTP request.".to_string());
                }
            };

            match method_str {
                GET => GET,
                POST => POST,
                _ => return Err(format!("HTTP method \"{}\" is not recognized.", method_str)),
            }
        };

        let target = {
            if let Some(method_str) = start_line_parts.next() {
                method_str.to_string()
            } else {
                return Err("Could not read target of HTTP request.".to_string());
            }
        };

        let version = {
            let version_str = {
                if let Some(version_str) = start_line_parts.next() {
                    version_str
                } else {
                    return Err("Could not read version of HTTP request.".to_string());
                }
            };

            match version_str {
                HTTP_1_0 => HTTP_1_0,
                HTTP_1_1 => HTTP_1_1,
                _ => {
                    return Err(format!(
                        "HTTP version \"{}\" is not recognized.",
                        version_str
                    ))
                }
            }
        };

        Ok(Request {
            method,
            target,
            version,
        })
    }
}