From 7dccf6e8684f767b6e377d627b36ccf493947f1e Mon Sep 17 00:00:00 2001 From: "Alex Xu (Hello71)" Date: Sun, 3 Oct 2021 15:03:42 -0400 Subject: Initial commit --- .gitignore | 5 + Makefile | 82 +++++++++++++++ analytics/analytics.go | 117 +++++++++++++++++++++ analytics/analytics.service | 28 +++++ analytics/analytics.socket | 8 ++ analytics/go.mod | 18 ++++ analytics/go.sum | 181 +++++++++++++++++++++++++++++++++ inliner.py | 50 +++++++++ resume/resume.css | 241 ++++++++++++++++++++++++++++++++++++++++++++ resume/resume.html | 230 ++++++++++++++++++++++++++++++++++++++++++ resume/resume.js | 51 ++++++++++ 11 files changed, 1011 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 analytics/analytics.go create mode 100644 analytics/analytics.service create mode 100644 analytics/analytics.socket create mode 100644 analytics/go.mod create mode 100644 analytics/go.sum create mode 100755 inliner.py create mode 100644 resume/resume.css create mode 100644 resume/resume.html create mode 100644 resume/resume.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93cd0b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/analytics/analytics +/out +/static +*.woff2 +*.otf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e813d30 --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +MAKEFLAGS += -R -r +HTML_MINIFIER_FLAGS := \ + --collapse-boolean-attributes \ + --collapse-whitespace \ + --decode-entities \ + --minify-css \ + --minify-js \ + --remove-attribute-quotes \ + --remove-comments \ + --remove-optional-tags \ + --remove-redundant-attributes \ + --remove-tag-whitespace \ + --sort-attributes \ + --sort-class-name \ + --ignore-custom-fragments 'clamp([^;]*);' \ + --trim-custom-fragments + +WFS ?= ~/wfs/wfs.py +WFS_FLAGS := \ + --desubroutinize \ + --layout-scripts=latn \ + --layout-features-=frac,locl \ + --name-IDs= \ + --drop-tables+=gasp,prep + +deployhosts := pink red blue + +staticall := $(patsubst static/%,out/%,$(shell find static)) +staticdirs := $(sort $(dir $(staticall))) +staticfiles := $(filter-out $(staticdirs),$(staticall)) + +tocomp := $(filter %.txt %.html %.js %.css,$(staticfiles)) out/resume/index.html + +fonts := resume/EBGaramond-Italic.otf resume/EBGaramond-Regular.otf resume/EBGaramond-Medium.otf +fontssubset := $(fonts:.otf=.subset.woff2) + +all: $(staticall) out/resume $(tocomp:=.br) $(tocomp:=.gz) + +out/%: static/% + install -Dpm644 $< $@ + +%.br: % + brotli -Zc $< > $@ + +%.gz: % + zopfli -c $< > $@ + +$(staticdirs) out/resume &: + mkdir -p $@ + +out/%.html: static/%.html | $(@D) + html-minifier $(HTML_MINIFIER_FLAGS) $< -o $@ + +out/resume/index.html: resume/resume.html resume/resume.js resume/resume.css $(fontssubset) | out/resume + ./inliner.py $< | html-minifier $(HTML_MINIFIER_FLAGS) > $@ + +$(fontssubset) &: resume/resume.html $(fonts) + cd $( 1 { + httpEnd(w, r, "too many urls", 400) + } else { + http.Redirect(w, r, url[0], 303) + _, err = conn.Exec(context.Background(), "insert into events(created_ts, ip_addr, kind, details) values ($1, $2, $3, $4)", time.Now(), r.RemoteAddr, "r", url[0]) + if err != nil { + log.Printf("%v 0 303 db error: %s", r.RemoteAddr, err) + return + } + log.Printf("%v 0 303 ok", r.RemoteAddr) + } + } else { + httpEnd(w, r, "missing url for GET", 400) + } + return + } + if r.Method != "POST" { + httpEnd(w, r, "method not allowed: "+r.Method, 405) + return + } + if r.ContentLength <= 0 { + httpEnd(w, r, "length required", 411) + return + } + if r.ContentLength > 1048576 { + httpEnd(w, r, "too much data", 413) + return + } + + data, err := io.ReadAll(r.Body) + if err != nil { + httpEnd(w, r, "read error", 400) + return + } + var events []Event + if err := json.Unmarshal(data, &events); err != nil { + log.Print(err) + httpEnd(w, r, "bad json", 400) + return + } + + _, err = conn.CopyFrom( + r.Context(), + pgx.Identifier{"events"}, + []string{"created_ts", "ip_addr", "kind", "details"}, + pgx.CopyFromSlice(len(events), func(i int) ([]interface{}, error) { + return []interface{}{time.Unix(events[i].Created_ts/1000, events[i].Created_ts%1000*1000000), r.RemoteAddr, events[i].Kind, events[i].Details}, nil + }), + ) + if err != nil { + log.Printf("%v %d 500 db error: %s", r.RemoteAddr, r.ContentLength, err) + http.Error(w, "db error", 500) + } else { + httpEnd(w, r, "ok", 200) + } + }) + + l, err := getListener() + if err != nil { + log.Fatal(err) + } + fcgi.Serve(l, nil) +} diff --git a/analytics/analytics.service b/analytics/analytics.service new file mode 100644 index 0000000..461a940 --- /dev/null +++ b/analytics/analytics.service @@ -0,0 +1,28 @@ +[Unit] +Description=analytics server + +[Service] +Type=simple +ExecStart=/usr/sbin/analytics + +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +CapabilityBoundingSet= +PrivateDevices=yes +PrivateNetwork=yes +PrivateTmp=yes +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectKernelLogs=yes +ProtectSystem=strict +RestrictAddressFamilies=AF_UNIX +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +SystemCallFilter=@system-service diff --git a/analytics/analytics.socket b/analytics/analytics.socket new file mode 100644 index 0000000..072ac0b --- /dev/null +++ b/analytics/analytics.socket @@ -0,0 +1,8 @@ +[Unit] +Description=analytics socket + +[Socket] +ListenStream=/run/analytics.sock + +[Install] +WantedBy=sockets.target diff --git a/analytics/go.mod b/analytics/go.mod new file mode 100644 index 0000000..7c8aaf2 --- /dev/null +++ b/analytics/go.mod @@ -0,0 +1,18 @@ +module alxu.ca/analytics + +go 1.17 + +require ( + blitiri.com.ar/go/systemd v1.1.0 + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.10.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.8.1 // indirect + github.com/jackc/pgx/v4 v4.13.0 + github.com/jackc/puddle v1.1.4 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/analytics/go.sum b/analytics/go.sum new file mode 100644 index 0000000..92d6ee0 --- /dev/null +++ b/analytics/go.sum @@ -0,0 +1,181 @@ +blitiri.com.ar/go/systemd v1.1.0 h1:AMr7Ce/5CkvLZvGxsn/ZOagzFf3zU13rcgWdlbWMQ+Y= +blitiri.com.ar/go/systemd v1.1.0/go.mod h1:0D9Ttrh+TX+WuKQ/dJpdhFND7NYy505v6jhsWrihmPY= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= +github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= +github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= +github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.4 h1:5Ey/o5IfV7dYX6Znivq+N9MdK1S18OJI5OJq6EAAADw= +github.com/jackc/puddle v1.1.4/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/inliner.py b/inliner.py new file mode 100755 index 0000000..b37c275 --- /dev/null +++ b/inliner.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +import base64 +import mimetypes +import os.path +import re +import sys + +ABSURL_RE = re.compile(r'''^(?:[a-z0-9+.-]+:|/)''') +def repl(func, base): + def g(m): + urlstr = m.group(1) + if urlstr[0] in ('"', "'"): + urlstr = urlstr[1:-1] + if ABSURL_RE.match(urlstr): + return m.group(0) + try: + return func(os.path.join(base, urlstr)) + except FileNotFoundError: + return m.group(0) + return g + +SCRIPT_RE = re.compile(r'''''') +def scriptrepl(urlstr): + with open(urlstr, 'r') as f: + script = f.read() + if '' in script: + raise Exception(' in script') + return f'' + +CSS_RE = re.compile(r'''''') +def cssrepl(urlstr): + with open(urlstr, 'r') as f: + return '' + +URL_RE = re.compile(r'''\burl\(("[^"]+"|'[^']+'|[^)]+)\)''') +def urlrepl(urlstr): + with open(urlstr, 'rb') as f: + return ('url(data:' + mimetypes.guess_type(urlstr)[0] + + ';base64,' + base64.b64encode(f.read()).decode('ascii') + ')') + +if __name__ == '__main__': + mimetypes.add_type('font/woff2', '.woff2') + base = os.path.dirname(sys.argv[1]) + with open(sys.argv[1], 'r') as f: + buf = f.read() + buf = SCRIPT_RE.sub(repl(scriptrepl, base), buf) + buf = CSS_RE.sub(repl(cssrepl, base), buf) + buf = URL_RE.sub(repl(urlrepl, base), buf) + sys.stdout.write(buf) diff --git a/resume/resume.css b/resume/resume.css new file mode 100644 index 0000000..04c8431 --- /dev/null +++ b/resume/resume.css @@ -0,0 +1,241 @@ +@font-face { + font-family: "EB Garamond"; + src: url(EBGaramond-Regular.subset.woff2); +} + +@font-face { + font-family: "EB Garamond"; + font-weight: 500; + src: url(EBGaramond-Medium.subset.woff2); +} + +@font-face { + font-family: "EB Garamond"; + font-style: italic; + src: url(EBGaramond-Italic.subset.woff2); +} + +* { + box-sizing: border-box; +} +body { + margin: .8em auto; + max-width: 90%; + width: 50em; + position: relative; + left: clamp(-2em,calc(25em - 45%),0em); + display: grid; + grid-template-columns: auto auto; + + font-family: "EB Garamond", serif; + font-size: 16px; + line-height: 1.38; + overflow-wrap: break-word; + text-rendering: optimizeLegibility; + background: #fff; + color: #333; +} + +address, .right { + align-self: center; +} +.left { + padding-right: .9rem; + text-align: right; + color: #246d84; +} +.right { + border-left: solid 3px #246d84; + padding: .2rem 0 0 1rem; +} +/* firefox prints grid margin wrong */ +.gap { + margin-top: .8rem; + grid-column: 1/-1; +} +.entry-header > :nth-child(1) { + flex: 60%; + max-width: max-content; + margin-right: auto; +} +.taddr { + grid-row: 1; + align-self: center; +} +b { + font-weight: 500; + font-size: 1.1em; +} +.date { + text-align: right; + margin-left: 1.2em; + max-width: max-content; + flex: 10 0 auto; + width: min-content; +} +.title { + line-height: 1.22; +} +.entry-header { + display: flex; +} +section + section { + margin-top: .7em; +} +address, .h1s { + color: #666; +} +address { + font-size: .9em; +} +.icon { + vertical-align: middle; + margin-bottom: .5ex; +} +h1, h2, h3, h4, p, ul { + margin: 0; +} +h1, h2, h3, h4 { + font-weight: normal; +} +h1 { + font-size: 3em; + margin-top: -.5ex; +} +h4 { + display: inline; +} +ul { + padding: 0; + list-style: none; +} +ul:not(.fakelist) > li { + margin-left: .1em; + /* separate list-style-type for safari <14.1 support */ + list-style-type: '◦ '; + list-style-image: url("data:image/svg+xml,"); + list-style-position: inside; +} +a { + color: #06c; + text-decoration: none; +} +a:visited { + color: #0f00b0; +} +.date-punct, .hide, .printcol { + display: none; +} +.nb { + display: inline-block; +} +abbr { + text-decoration: none; +} +.h1s { + font-size: 1.4em; + font-style: italic; +} +@media (min-width: 25em) { + .left-heavy-columns { + columns: 2 14em; + margin-right: -1.4em; + } +} +@media (max-width: 30em) { + body { + display: block; + } + h1 { + margin-bottom: -.2ex; + } + .h1s { + margin-bottom: .6ex; + } + .title, .left address { + text-align: center; + } + .left address { + columns: 2; + } + .left { + text-align: center; + padding: 0; + min-width: 0; + } + .left:not(.taddr) { + border-bottom: solid; + margin-bottom: .2rem; + } + .gap { + margin-bottom: 1rem; + } + .right { + border-left: none; + padding: 0; + } + .title { + padding: 0; + } + .nb { + display: initial; + } +} +@media (max-width: 21em) { + header, .entry-header { + display: block; + } + .date { + text-align: left; + margin: 0; + width: auto; + } + .date-punct { + display: inline; + } +} +@media print { + .noprint { + display: none; + } + body { + position: static; + margin: 0; + padding: 0; + width: 100%; + max-width: 100%; + font-size: 13px; + line-height: 1.28; + color: #000; + -webkit-print-color-adjust: exact; + } + /* workaround: safari doesn't support css columns in print */ + .left-heavy-columns { + display: none; + } + .printcol { + display: inline-block; + } + section + section { + margin-top: .6em; + } + /* override print color for firefox */ + address, .h1s { + color: transparent; + text-shadow: 0 0 #555; + } + address svg { + color: #555; + } +} +@media (prefers-contrast: high) { + .h1s, address { + color: #555; + } +} +@media (prefers-contrast: low) { + body { + background: #ccc; + } +} +@page { margin: 7mm 26mm 7mm 18mm; } diff --git a/resume/resume.html b/resume/resume.html new file mode 100644 index 0000000..982ded8 --- /dev/null +++ b/resume/resume.html @@ -0,0 +1,230 @@ + + + + + Alex Xu's Resume + + + + + + + + + +
+

Alex Xu

+

Generalist software developer

+
+
+
+ +
+
+
+

Education

+
+
+
+

Lassonde School of Engineering, B.Sc., Hons. Computer Science, York University, GPA: 7.7/9.0 (A).

+
Sept. 2016–May 2020:
+
+
    +
  • Advanced Object Oriented Programming (A+)
  • +
  • Design and Analysis of Algorithms (A+)
  • +
  • Fundamentals of Data Structures (A+)
  • +
  • Computer Architecture (A)
  • +
  • Database Management Systems (A+)
  • +
  • Applied Cryptography (A+)
  • +
  • Mathematics of Cryptography (A)
  • +
  • Network Security (A+)
  • +
+
    +
  • Advanced Object Oriented Programming (A+)
  • +
  • Design and Analysis of Algorithms (A+)
  • +
  • Fundamentals of Data Structures (A+)
  • +
  • Computer Architecture (A)
  • +
+
    +
  • Database Management Systems (A+)
  • +
  • Applied Cryptography (A+)
  • +
  • Mathematics of Cryptography (A)
  • +
  • Network Security (A+)
  • +
+
+
+
+

Work

+
+
+
+

Ethica Channel Enablement Inc, Network Engineer.

+
Dec. 2018–Oct. 2019:
+
+

Developed several core projects and provided extensive consulting on C, Linux, git, and networking.

+
    +
  • built a Buildroot-based minimal Linux infrastructure for the launch of a multi-link VPN product
  • +
  • created a fully automated high-speed operating system installer for x86 systems using BusyBox sh
  • +
  • created a tool for remote Linux in-place replacement using POSIX sh and BusyBox
  • +
  • developed a GitLab CI process for generating the deployable image
  • +
+
+
+
+

York University, Undergraduate Student Research Award researcher.

+
May–Aug. 2018:
+
+

Co-developed a Django web platform for worldwide crowdsourced hydrographic data collaboration.

+
+
+
+

York University, Teaching assistant.

+
June–July 2017:
+
+

Taught lab sessions for 48-student Android/web development course.

+
+
+
+
+

Community

+
+ +
+
+
+
+
+ +
March 2020–present:
+
+

Reported, documented, and contributed fixes for issues including Operation not permitted in Docker, sh: write error: Invalid argument, and Raspberry Pi doesn't boot, 7 blinks. Proposed changes including -fno-plt for x86 and x86_64, compressing debuginfo, and reconsidering -Os.

+
+
+
+ +
September 2021:
+
+

Implemented correct global-dynamic TLS support, fixing musl compatibility.

+
+
+
+ +
July 2021:
+
+

Implemented copy_file_range support, shrinking Wine prefixes from 200 MB to less than 1 MB.

+
+
+
+

Linux kernel

+
2012–present:
+
+

Reported issues and submitted fixes: lkml, linuxlists. Diagnosed Cargo issue 9739 to a long-standing kernel bug, and fixed the underlying issue, resolving a deadlock in GNU Make and similar jobservers.

+
+
+
+ +
December 2020:
+
+

Discovered and reported an issue allowing full host device access from guests with virtiofsd enabled.

+
+
+
+ +
June 2020:
+
+

Built a concurrent Python web font subsetter with automatic pixel-perfect verification.

+
+
+
+ +
March 2020:
+
+

Built a concurrent Python pygments microservice, reducing TTFB from 1.1s to 0.15s on cgit.alxu.ca. Improved portability and security and reduced LOC by 66% by switching from http.server to aiohttp.

+
+
+
+ +
July 2016:
+
+

Built a C tunnel to simulate datagrams using TCP packets, fixing TCP-over-TCP overhead.

+
+
+
+ +
Aug. 2014–Sept. 2017:
+
+

Implemented cross-bit ptrace, migrated from qmake to CMake, and refactored code.

+
+
+
+ +
Sept. 2008:
+
+

Built a minimal .NET registry editor.

+
+
+
+

Personal

+
+
+
+

Resume

+
June 2020:
+
+

Rewrote my resume from LaTeX to modern web standards:

+
    +
  • significantly improved mobile-friendliness and blind accessibility using standard HTML and CSS
  • +
  • reduced transfer size from 390 kB to 30 kB using aggressive inlining and font subsetting
  • +
  • optimized loading time by minimizing size and reducing round trips using HTTP/2 server push
  • +
+
+
+
+

Skills

+
+

Alpine, Debian Linux; FreeBSD; Docker; Git; NGINX; WireGuard; TCP/IP; HTTP(S); DNS; SSH

+
+

Programming

+
+

C; C++; Go; Python; bash, POSIX sh; GNU make; HTML; CSS; JavaScript; SQL

+
+ + + diff --git a/resume/resume.js b/resume/resume.js new file mode 100644 index 0000000..3f33976 --- /dev/null +++ b/resume/resume.js @@ -0,0 +1,51 @@ +(function(w, d, a){ + 'use strict'; + if (d.documentMode) + ie.style.display = "block"; + else + ie.parentNode.removeChild(ie); + const css = d.styleSheets[0]; + if (w.safari) { + css.insertRule("@page{margin:10mm}", css.cssRules.length); + css.insertRule("@media print{body{margin:-2mm 0 -2mm 0;padding:0 15mm 0 7mm}}", css.cssRules.length); + } + else if (w.chrome) + css.insertRule("@page{margin-top:auto;margin-bottom:auto}", css.cssRules.length); + let t, p = []; + const f = () => { + clearTimeout(t); + navigator.sendBeacon('/analytics', new Blob([JSON.stringify(p)], {type: 'application/json'})); + p = []; + }; + const q = (kind, details) => { + clearTimeout(t); + p.push({created_ts: Date.now(), kind: kind, details: details}); + t = setTimeout(f, 2000); + }; + q('l', ''); + d[a]('visibilitychange', () => { + const vs = d.visibilityState; + q('v', vs); + if (vs === 'hidden') + f(); + }); + d[a]('click', e => { + const a = e.target.closest('a'); + if (a) + q('c', a.href); + }); + w[a]('beforeprint', () => { + for (let el of d.getElementsByTagName('a')) { + const h = el.href.replace(/javascript:location=(.*);void 0/, '$1'); + el.setAttribute('data-href', el.href); + if (h != el.href) el.href = eval(h); + else el.href = "https://alxu.ca/analytics?url=" + el.href.replace(/%/g, '%25').replace(/&/g, '%26').replace(/;/g, '%3B'); + } + }); + w[a]('afterprint', function () { + for (let el of d.getElementsByTagName('a')) { + el.href = el.getAttribute('data-href'); + el.removeAttribute('data-href'); + } + }); +}(window, document, 'addEventListener')); -- cgit v1.2.3-54-g00ecf