initial commit
This commit is contained in:
commit
12113c2ee1
10 changed files with 313 additions and 0 deletions
6
docker-compose.yml
Normal file
6
docker-compose.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
services:
|
||||||
|
blogsite:
|
||||||
|
image: custom/blogsite
|
||||||
|
build: ./src
|
||||||
|
networks:
|
||||||
|
- net
|
||||||
18
src/Dockerfile
Normal file
18
src/Dockerfile
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
FROM alpine:latest
|
||||||
|
WORKDIR /site
|
||||||
|
|
||||||
|
COPY ./in /site/in
|
||||||
|
|
||||||
|
RUN apk add git go lowdown busybox-extras
|
||||||
|
RUN mkdir -p /site/out /site/kew
|
||||||
|
RUN git clone https://stvnliu.me/git/steven/kew-custom.git /site/kew
|
||||||
|
RUN cd /site/kew && \
|
||||||
|
go build && \
|
||||||
|
cp ./kew /usr/local/bin/kew
|
||||||
|
RUN kew /site/in /site/out
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["httpd", "-f", "-p", "8080", "-h", "/site/out"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
8
src/in/index.md
Normal file
8
src/in/index.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
### Some things I've written on
|
||||||
|
|
||||||
|
- learning dutch
|
||||||
|
- using interesting technology
|
||||||
|
- reading some books
|
||||||
|
- other things in life in general
|
||||||
|
|
||||||
|
Copyright Zhongheng Liu 2026-2027
|
||||||
106
src/in/style.css
Normal file
106
src/in/style.css
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
:root {
|
||||||
|
--bg: #646c7f;
|
||||||
|
--fg: #fffde0;
|
||||||
|
--fg-link: #fff18f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* global */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--fg);
|
||||||
|
font-family: serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* header */
|
||||||
|
header {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 35px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
header a {
|
||||||
|
color: var(--fg);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.25rem;
|
||||||
|
align-items: start;
|
||||||
|
margin-right: 400px;
|
||||||
|
/* Desktop: article + sidebar */
|
||||||
|
grid-template-columns: 1fr 8fr; /* content flexible, nav fixed-ish */
|
||||||
|
grid-template-areas: "side content";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assign the semantic elements to grid areas */
|
||||||
|
article {
|
||||||
|
grid-area: content;
|
||||||
|
margin-left: 50px;
|
||||||
|
}
|
||||||
|
#side-bar {
|
||||||
|
grid-area: side;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: max-content;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
/* Mobile: stack them, keeping article first visually */
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
main {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-areas:
|
||||||
|
"side"
|
||||||
|
"content";
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.side-title {
|
||||||
|
font-size: 25px;
|
||||||
|
margin: 20px 0 8px 0;
|
||||||
|
color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#side-bar ul {
|
||||||
|
margin: 0 0 0 20px;
|
||||||
|
padding: 0px;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#side-bar li {
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* links */
|
||||||
|
a {
|
||||||
|
color: var(--fg-link);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 1px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-size: 25px;
|
||||||
|
color: var(--fg);
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* footer */
|
||||||
|
footer {
|
||||||
|
padding-top: 80px;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
13
src/in/taalstudie/de-eerste.md
Normal file
13
src/in/taalstudie/de-eerste.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Learning Dutch (de eerste)
|
||||||
|
date: 2025-07-27 06:43:17
|
||||||
|
tags:
|
||||||
|
- dutch
|
||||||
|
- language
|
||||||
|
---
|
||||||
|
# Eerste tekst in het Nederlands
|
||||||
|
Hallo 👋, in deze maanden ben ik op een taalverwerving reis van de Nederlandse taal.
|
||||||
|
|
||||||
|
Nu kun ik niet wat sommige mensen praten begrijpen, maar sommige dingen in de televisie afleveringen kan ik nu begrijp, in particuliere sommige grappige van De Avondshow met Arjen Lubach. Hij is een heel leuk mens, met een even beter humor.
|
||||||
|
|
||||||
|
Misschien ik heb in dit tekst een paar fouten maken, maar dat is goed, want in de toekomst ga ik dit gezien, en daarna moet ik hoeveel beter mijn Nederlands worden over deze eerste maanden aan de verwering reis kennen.
|
||||||
19
src/in/taalstudie/de-tweede.md
Normal file
19
src/in/taalstudie/de-tweede.md
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# De tweede tekst in Nederlands
|
||||||
|
|
||||||
|
Hoi allemal, ik heb een beetje meer geleerd van de taal sinds de laatste update,
|
||||||
|
en we zijn nu bijna in het einde van het jaar 2025, nog een dag en komt het jaar 2026 (Dit tekst was op 30 dec 2025 geschreven).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Tijdens het jaar, er waren veel nieuwe dingen die ik heb geleerd. Sinds de tweede kwart van het studiejaar, ik heb in een officiele cursus bij de universiteit geweest.
|
||||||
|
Van deze cursus heb ik belangrijke woorden een grammatica geleerd, een daarom kan ik nu dit tekst schrijven. Ik weet het al dat ik zal fouten maken in dit tekst.
|
||||||
|
Volgend jaar, ik zal in deze cursus (maar natuurlijk op een hoger niveau) blijven, zodat ik kan meer van de taal leren.
|
||||||
|
|
||||||
|
Is Nederlands moeilijk? Ik denk niet zo. Voor mij, het was beetje makkelijker want ik ben al goed in Engels, en heb een paar Duitse woorden geweten. Dus ik heb al een goede basis om Nederlands te leren.
|
||||||
|
|
||||||
|
Maar ik weet niet zo veel van de cultuur. Nederlandse cultuur is veel interessante, want het normale van de samenleving is iets anders van dat in Griekenland, waar ik heb voor vier jaar gewoont.
|
||||||
|
|
||||||
|
Feestjes in Nederland zijn anders ook, met Sinterklaas (die is een feestje anders van Kerstmis). In Delft, er komt een markt elke week op donderdag en zaterdag, in het centrum.
|
||||||
|
Maar in het bijzonder, er was een grotere markt op Sinterklaas. Op de dag, ik heb een smakelijke portie kibbeling gegeten van een vishandel die was in het markt. Er was ook muziek, en lopend een paar Pietjes overal het markt.
|
||||||
|
|
||||||
|
In het einde, leven in Delft gaat nog goed in dit jaar. Ik heb alle cursusen van mijn eerste kwart geslaagd, en ik houd van alles in Nederland, behalve het weer. Ik hoop dat volgend jaar gaat nog beter!
|
||||||
3
src/in/taalstudie/index.md
Normal file
3
src/in/taalstudie/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Mijn teksten in het Nederlands
|
||||||
|
|
||||||
|
Volgende lijst zijn teksten die ik heb gescheven in mijn studie van de Nederlandsche taal. Natuurlijk zal er fouten in de teksten zijn, maar dat is normaal tijdens taalstudie hé?
|
||||||
86
src/in/tech/java-sorting-with-comparables.md
Normal file
86
src/in/tech/java-sorting-with-comparables.md
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
# Sorting with Comparables
|
||||||
|
Any class in Java that implements `Comparable<T>`can be compared to another object of class `T` via a public static method. This allows reference types to be compared to by a custom behaviour.
|
||||||
|
|
||||||
|
## Sorting collections with a `Comparable`
|
||||||
|
`Collection<T>.sort()` sorts the collection by calling the `T.compareTo(T other)` method, where `T implements Comparable`.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Data implements Comparable<Data> {
|
||||||
|
private String name;
|
||||||
|
...
|
||||||
|
@Override
|
||||||
|
public int compareTo(Data other) {
|
||||||
|
return this.name.compareTo(other.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
$$
|
||||||
|
\text{compareTo}: A \times A \rightarrow \mathbb{Z}
|
||||||
|
$$
|
||||||
|
The implementation of `compareTo` satisfies the following
|
||||||
|
$$
|
||||||
|
\text{compareTo}(x, y) =
|
||||||
|
\begin{cases}
|
||||||
|
a \in \mathbb{Z}^- & \text{if $x > y$}\\
|
||||||
|
0 & \text{if $x = y$}\\
|
||||||
|
b \in \mathbb{Z}^+ & \text{if $x > y$}\\
|
||||||
|
\end{cases}
|
||||||
|
$$
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
> [!important]
|
||||||
|
> To control (customize) the sorting mechanism, you need to have ownership of the class that implements the `Comparable` interface. This is not always possible. See `List<Integer>` sorting example.
|
||||||
|
## Custom sorting for arbitrary class `T` with a `Comparator<T>
|
||||||
|
`Collection<T>.sort(Comparator<T>)` sorts the `Collection` by passing value of type `T` into a `Comparator<T>`.
|
||||||
|
|
||||||
|
We can implement a custom `Comparator` by an anonymous class:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Comparator<T> cmp = new Comparator<T>() {
|
||||||
|
@Override
|
||||||
|
public int compare(T x, T y) {
|
||||||
|
return x.compareTo(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
>[!note] Lambda comparators
|
||||||
|
>A comparator can also be defined as a lambda function:
|
||||||
|
>```java
|
||||||
|
Comparator<T> cmp1 = (x, y) -> x.compareTo(y);
|
||||||
|
>```
|
||||||
|
|
||||||
|
`cmp1` has the same behaviour:
|
||||||
|
$$\verb|cmp(x, y)| \equiv \verb|cmp1(x, y)|$$
|
||||||
|
|
||||||
|
A use for this is inverting the condition of default behaviour in a class which implementation you cannot mutate.
|
||||||
|
|
||||||
|
Consider now the example of sorting a `List<Integer>`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<Integer> list = new ArrayList<>();
|
||||||
|
list.add(1);
|
||||||
|
list.add(2);
|
||||||
|
list.add(3);
|
||||||
|
list.add(4);
|
||||||
|
```
|
||||||
|
|
||||||
|
We want to sort this list of integers from largest to smallest, whereas the default behaviour from `Integer implements Comparable` is from the smallest to largest.
|
||||||
|
|
||||||
|
Now we can apply a custom `Comparator<T>` such that the behaviour is inverted.
|
||||||
|
|
||||||
|
```java
|
||||||
|
list.sort(new Comparator<Integer>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Integer x, Integer y) {
|
||||||
|
// We invert the comparison result of x.compareTo(y).
|
||||||
|
// swaps from smallest-to-largest to largest-to-smallest
|
||||||
|
return -x.compareTo(y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
System.out.println(list); // [4, 3, 2, 1]
|
||||||
|
```
|
||||||
26
src/in/tech/nginx-header-processing.md
Normal file
26
src/in/tech/nginx-header-processing.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# When is it modified, Nginx?
|
||||||
|
> RFC 2616 specifies the behaviour for a web server to respond according to an `if_modified_since` header, but it doesn't work correctly with Nginx. How and why is that so?
|
||||||
|
|
||||||
|
*TL;DR: The default Nginx behaviour is `if_modified_since exact;` rather than the more RFC-compliant `if_modified_since before;`, so only an exact date & time match would produce a `304`.*
|
||||||
|
|
||||||
|
One bright, sunshiny day I sat down in front of the computer and put in the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --http1.1 -I --header 'If-Modified-Since: Mon, 01 Jan 1970 08:00:00 GMT' https://stvnliu.me/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
The expectation was that this would produce a `304 Not Modified` HTTP response, as it was specified in the relevant RFC standard. Instead, I was abhorred to discover the status code being a `200 OK` in the following response:
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Server: nginx/1.29.1
|
||||||
|
Date: Wed, 17 Dec 2025 13:50:21 GMT
|
||||||
|
Content-Type: text/html
|
||||||
|
Content-Length: 1250
|
||||||
|
Last-Modified: Thu, 04 Dec 2025 22:59:21 GMT
|
||||||
|
Connection: keep-alive
|
||||||
|
ETag: "69321249-4e2"
|
||||||
|
Accept-Ranges: bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
A quick dive down the Nginx tracker led to [this wontfix issue](https://trac.nginx.org/nginx/ticket/93), in which a developer responds to the question by stating that even though `if_modified_since before;` adheres more to the RFC standards, they deemed the `exact` behaviour to be much more reliable, i.e. the trade-off for unnecessarily re-fetched content in `200 OK` responses is sufficiently small compared to incorrect behaviour of serving stale content in false-positive `304` events.
|
||||||
28
src/in/template.html
Normal file
28
src/in/template.html
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{TITLE}}</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/style.css" type="text/css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1><a href="/index.html">~blog.stvnliu.me</a></h1>
|
||||||
|
</header>
|
||||||
|
<main id="content">
|
||||||
|
<nav id="side-bar">
|
||||||
|
{{NAV}}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
{{CONTENT}}
|
||||||
|
<footer>
|
||||||
|
<p>{{FOOTER}}</p>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue