feat: aliases (#5)
All checks were successful
/ deploy (push) Successful in 39s

Reviewed-on: #5
This commit is contained in:
ange 2025-04-05 10:17:05 +00:00
parent c4e53afdf1
commit abd9218561
19 changed files with 242 additions and 176 deletions

View File

@ -1,4 +1,4 @@
FROM docker.io/golang:1.23 AS build
FROM docker.io/golang:1.24 AS build
WORKDIR /build/
COPY go.mod go.sum .
RUN go mod download
@ -11,5 +11,6 @@ COPY html/ html/
COPY css/ css/
COPY static/ static/
COPY tmpl/ tmpl/
COPY aliases.txt aliases.txt
EXPOSE 3000
CMD ["./app"]

5
aliases.txt Normal file
View File

@ -0,0 +1,5 @@
ssh /static/ssh
pgp /static/pgp.asc
termux.sh https://git.gmoker.com/ange/termux/raw/branch/main/install.sh
dotfiles.sh https://git.gmoker.com/ange/.dotfiles
arch.sh https://git.gmoker.com/ange/arch

View File

@ -1,11 +1,19 @@
@media print {
@page {
size: A4;
size: portrait;
margin: 5mm;
}
@page {
size: A4;
size: portrait;
margin: 5mm;
}
#menu, #credits {
display: none;
}
#credits, #menu {
display: none;
}
#main {
margin: 0;
}
#nav {
float: right;
}
}

View File

@ -1,176 +1,176 @@
/* https://suckless.org/pub/style.css */
html {
overflow-y: scroll;
overflow-y: scroll;
}
body {
background-color: #fff;
color: #000;
font-family: sans-serif;
padding: 0;
margin: 0;
background-color: #fff;
color: #000;
font-family: sans-serif;
padding: 0;
margin: 0;
}
pre, code {
margin: 0;
margin: 0;
}
a {
color: #005386;
color: #005386;
}
#header a, #nav a, #menu a {
text-decoration: none;
text-decoration: none;
}
#nav a:hover {
background-color: #ddd;
background-color: #ddd;
}
#menu {
clear: both;
color: #069;
overflow: hidden;
background-color: #17a;
padding: 0.7ex;
border-top: 1px solid #ccc;
border-bottom: 1px solid #069;
clear: both;
color: #069;
overflow: hidden;
background-color: #17a;
padding: 0.7ex;
border-top: 1px solid #ccc;
border-bottom: 1px solid #069;
}
#menu a {
padding: 0.5ex 1ex;
color: #fff;
padding: 0.5ex 1ex;
color: #fff;
}
#menu a:hover {
background-color: #069;
background-color: #069;
}
#header {
background-color: #eee;
clear: both;
color: #555;
font-size: 1.78em;
padding: 0.7ex 0.7ex 0.7ex 0.7em;
background-color: #eee;
clear: both;
color: #555;
font-size: 1.78em;
padding: 0.7ex 0.7ex 0.7ex 0.7em;
}
#headerLink {
color: #17a;
margin-left: 5px;
color: #17a;
margin-left: 5px;
}
h1 {
margin: 1em 1ex 0.5ex 0;
font-size: 1.4em;
margin: 1em 1ex 0.5ex 0;
font-size: 1.4em;
}
h2 {
margin: 1em 1ex 0.5ex 0;
font-size: 1.3em;
margin: 1em 1ex 0.5ex 0;
font-size: 1.3em;
}
h3 {
margin: 1em 1ex 0.5ex 0;
font-size: 1.0em;
margin: 1em 1ex 0.5ex 0;
font-size: 1.0em;
}
h4 {
margin: 1em 1ex 0.5ex 0;
font-size: 0.9em;
margin: 1em 1ex 0.5ex 0;
font-size: 0.9em;
}
#headerSubtitle {
font-size: 0.75em;
font-style: italic;
margin-left: 1em;
color: #fff;
font-size: 0.75em;
font-style: italic;
margin-left: 1em;
color: #fff;
}
#content {
clear: both;
margin: 0;
padding: 0;
clear: both;
margin: 0;
padding: 0;
}
#nav {
float: left;
margin: 0 1px 0 0;
padding: 1em 0;
border-right: 1px dotted #ccc;
width: 200px;
float: left;
margin: 0 1px 0 0;
padding: 1em 0;
border-right: 1px dotted #ccc;
width: 200px;
}
#nav ul {
margin: 0;
padding: 0;
margin: 0;
padding: 0;
}
#nav li {
list-style: none;
padding: 0;
margin: 0;
list-style: none;
padding: 0;
margin: 0;
}
#nav li ul {
padding-left: 0.6em !important;
padding-left: 0.6em !important;
}
#nav li a {
display: block;
margin: 0;
padding: 0.8ex 2em 0.8ex 1em;
display: block;
margin: 0;
padding: 0.8ex 2em 0.8ex 1em;
}
#main {
margin: 0 0 0 200px;
max-width: 50em;
padding: 1.5em;
margin: 0 0 0 200px;
max-width: 50em;
padding: 1.5em;
}
.left {
float: left;
margin: 0;
padding: 0;
float: left;
margin: 0;
padding: 0;
}
.right {
float: right;
margin: 0;
padding: 0;
float: right;
margin: 0;
padding: 0;
}
.hidden {
display: none;
display: none;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #000;
color: #bdbdbd;
}
#menu {
border-top: 1px solid #222;
}
#header {
background-color: #111;
}
#nav a:hover {
background-color: #222;
}
blockquote, pre, code {
background-color: #111;
border-color: #222;
}
a {
color: #56c8ff;
}
#main img[src$=svg] {
filter: invert(1);
}
body {
background-color: #000;
color: #bdbdbd;
}
#menu {
border-top: 1px solid #222;
}
#header {
background-color: #111;
}
#nav a:hover {
background-color: #222;
}
blockquote, pre, code {
background-color: #111;
border-color: #222;
}
a {
color: #56c8ff;
}
#main img[src$=svg] {
filter: invert(1);
}
}
footer {
position: fixed;
bottom: 0;
position: fixed;
bottom: 0;
}

2
go.mod
View File

@ -1,3 +1,3 @@
module main
go 1.23.3
go 1.24.1

View File

@ -2,68 +2,57 @@
<html>
<head>
{{template "head.tmpl" .}}
<script src="static/copy.js"></script>
<script src="copy.js"></script>
</head>
<body>
{{template "header.tmpl" .}}
<div id="content">
<!--
<div id="nav">
<ul>
<li><a href="#matrix">Matrix</a></li>
<li><a href="#discord">Discord</a></li>
<li><a href="#phone">Phone (France)</a></li>
<li><a href="#email">Email</a></li>
<li><a href="#wa">WhatsApp</a></li>
</ul>
</div>
-->
<div id="main">
<table>
<tr>
<td>Matrix (Element)</td>
<td id="matrix"><a href="//app.element.io/#/user/@ange:gmoker.com" target="_blank" rel="noreferrer noopener">@ange:gmoker.com</a></td>
<td id="matrix">{{template "matrix"}}</td>
<td><button onclick="copyElem('matrix')">Copy</button></td>
</tr>
<tr>
<td>Discord</td>
<td id="discord">@elrilio</td>
<td id="discord">{{template "discord"}}</td>
<td><button onclick="copyElem('discord')">Copy</button></td>
</tr>
<tr>
<td>Phone (France)</td>
<td id="phone"><a href="tel:+33582951623" target="_blank" rel="noreferrer noopener">+33 5 82 95 16 23</a></td>
<td id="phone">{{template "phone"}}</td>
<td><button onclick="copyElem('phone', / /g)">Copy</button></td>
</tr>
<tr>
<td>Email</td>
<td id="email"><a href="mailto:ange@yw5n.com" target="_blank" rel="noreferrer noopener">ange@yw5n.com</a></td>
<td id="email">{{template "email"}}</td>
<td><button onclick="copyElem('email')">Copy</button></td>
</tr>
<tr>
<td>WhatsApp</td>
<td id="wa"><a href="//wa.me/6285333559453" target="_blank" rel="noreferrer noopener">+62 853-3355-9453</a></td>
<td id="wa">{{template "wa"}}</td>
<td><button onclick="copyElem('wa', /[ |-]/g)">Copy</button></td>
</tr>
<tr>
<td>LinkedIn</td>
<td id="in"><a href="https://www.linkedin.com/in/angedhn" target="_blank" rel="noreferrer noopener">www.linkedin.com/in/angedhn</a></td>
<td id="in">{{template "in"}}</td>
<td><button onclick="copyElem('in')">Copy</button></td>
</tr>
<tr>
<td>PGP</td>
<td id="pgp"><a href="/static/pgp.asc" target="_blank" rel="noreferrer noopener">pgp.asc</a></td>
<td id="pgp">{{template "pgp"}}</td>
<td><button onclick="copyFile('pgp')">Copy</button></td>
</tr>
<tr>
<td>SSH</td>
<td id="ssh"><a href="/static/ssh" target="_blank" rel="noreferrer noopener">ssh</a></td>
<td id="ssh">{{template "ssh"}}</td>
<td><button onclick="copyFile('ssh')">Copy</button></td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -8,14 +8,11 @@
{{template "header.tmpl" .}}
<div id="content">
<div id="nav">
<!--
<ul>
<li><a href="#formation">Formation</a></li>
<li><a href="#experience">Experience</a></li>
<li><a href="#competences">Competences</a></li>
</ul>
-->
<img src="static/2404.webp" alt="Ange DUHAYON" width="128">
<img src="static/2404.webp" alt="Ange DUHAYON" width="128"><br>
{{template "phone"}}<br>
<a href="//yw5n.com">yw5n.com<br>
{{template "email"}}<br>
{{template "in"}}<br>
</div>
<div id="main">
@ -28,19 +25,22 @@
</ul>
<h2 id="experience">EXPERIENCE</h2>
<h3><b>Freelance</b> - DevOps</h3>
<h3><b>Freelance</b> - DevOps Engineer</h3>
SEPTEMBER 2023 - TODAY
<ul>
<li>Server setup (Debian, Docker/K8S, web server, customer application)</li>
<li>CI/CD, real-time monitoring</li>
<li>Docker / K8S</li>
<li>GitHub Actions</li>
<li>Prometheus</li>
<li>Python / Go backend</li>
</ul>
<h3><b>Selfhost</b> - DevOps</h3>
<h3><b>Selfhost</b> - DevOps Engineer</h3>
JANUARY 2022 - TODAY
<ul>
<li>Debian, K8S, Gitea CI/CD</li>
<li>Bare metal K8S</li>
<li>Gitea Actions</li>
<li>Nextcloud, Matrix.org, Jellyfin, SearXNG</li>
</ul>
<h3><b>EPITECH CodingClub association, Toulouse</b> - Cobra</h3>
<h3><b>EPITECH CodingClub association, Toulouse</b> - Trainer</h3>
JANUARY 2021 - JUNE 2024
<ul>
<li>Coding workshops for high school students</li>
@ -48,20 +48,21 @@
<h3><b>Predicloud, Toulouse</b> - DevOps SRE</h3>
JUNE 2022 - JULY 2023
<ul>
<li>CI/CD, K8S monitoring</li>
<li>Authentication solution (LDAP, SSO, Webhook) + Python backend</li>
<li>Mail server (Postfix, Dovecot)</li>
<li>Customer communication and troubleshooting</li>
<li>GitLab CI</li>
<li>Prometheus, Grafana</li>
<li>OpenLDAP + Python frontend, Keycloak, K8S webhook</li>
<li>Postfix, Dovecot</li>
<li>Customer troubleshooting</li>
</ul>
<h2 id="competences">COMPETENCES</h2>
<table>
<tr><th>LANGUAGES</th> <th>SOFTWARE</th></tr>
<tr><td>C/C++</td> <td>*nix</td></tr>
<tr><td>Python</td> <td>Git</td></tr>
<tr><td>Bash</td> <td>Vim</td></tr>
<tr><td>Go</td> <td>Docker</td></tr>
<tr><td>JS</td> <td>K8S</td></tr>
<tr><th>LANGUAGES</th><th>SOFTWARE</th></tr>
<tr><td>C/C++</td> <td>*nix</td></tr>
<tr><td>Python</td> <td>Git</td></tr>
<tr><td>Bash</td> <td>Vim</td></tr>
<tr><td>Go</td> <td>Docker</td></tr>
<tr><td>JS</td> <td>K8S</td></tr>
</table>
</div>

View File

@ -3,32 +3,35 @@ set -o pipefail
function kapply() {
for f in "$@"; do
kubectl apply -f \
<(envsubst "$(env | xargs printf '$%s ')" < "manifests/$f")
kubectl apply -f <(envsubst < "manifests/$f")
done
}
}; export -f kapply
function kcreatesec() {
kubectl create secret generic --save-config --dry-run=client -oyaml "$@" | kubectl apply -f-
}
kubectl create secret generic --dry-run=client -oyaml "$@" | kubectl replace -f-
}; export -f kcreatesec
function kcreatecm() {
kubectl create configmap --dry-run=client -oyaml "$@" | kubectl apply -f-
}
kubectl create configmap --dry-run=client -oyaml "$@" | kubectl replace -f-
}; export -f kcreatecm
function kgseckey() {
local sec="$1"; shift
local key="$1"; shift
kubectl get secret "$sec" -o jsonpath="{.data.$key}" | base64 -d
}
if ! kubectl get secret "$sec" -ojson | jq -re ".data.\"$key\" // empty" | base64 -d; then
return 1
fi
}; export -f kgseckey
function kgcmkey() {
local cm="$1"; shift
local cm="$1"; shift
local key="$1"; shift
kubectl get configmap "$cm" -o jsonpath="{.data.$key}"
}
if ! kubectl get configmap "$cm" -ojson | jq -re ".data.\"$key\" // empty"; then
return 1
fi
}; export -f kgcmkey
kapply common/app.yaml

View File

@ -1,4 +1,5 @@
#!/bin/bash -e
set -o pipefail
export NB_REPLICAS=1

View File

@ -1,4 +1,5 @@
#!/bin/bash -e
set -o pipefail
export NB_REPLICAS=3

25
src/aliases.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"log"
"os"
"strings"
)
var ALIASES map[string]string
func generateAliases() {
f, err := os.ReadFile("aliases.txt")
if err != nil {
log.Fatal(err)
}
ALIASES = make(map[string]string)
for l := range strings.SplitSeq(string(f), "\n") {
sp := strings.Fields(l)
if len(sp) == 2 {
ALIASES[sp[0]] = sp[1]
}
}
}

View File

@ -7,6 +7,7 @@ import (
func main() {
http.HandleFunc("/", route)
generateAliases()
generateTmpl()
if err := http.ListenAndServe(":3000", nil); err != nil {
log.Fatal(err)

View File

@ -17,7 +17,7 @@ var routes = []struct {
}{
{[]string{"GET"}, url(""), index},
{[]string{"GET"}, url("/static/.+"), static},
{[]string{"GET"}, url("/(.+\\.css)"), css},
{[]string{"GET"}, url("/(.+\\.(css|js))"), file},
{[]string{"GET"}, url("/([^/]+)"), html},
}
@ -42,8 +42,8 @@ func route(w http.ResponseWriter, r *http.Request) {
}
fmt.Println(r.Method, r.URL.Path)
rt.handler(w, r.WithContext(
context.WithValue(r.Context(), URLParam{}, matches[1:])),
)
context.WithValue(r.Context(), URLParam{}, matches[1:]),
))
return
}
}

View File

@ -12,10 +12,10 @@ var TMPL map[string][]byte
func generateTmpl() {
files, _ := filepath.Glob("html/*.html")
re := regexp.MustCompile("html/(.+).html")
names := make([]string, len(files))
pages := make([]string, len(files))
for i, f := range files {
names[i] = re.FindStringSubmatch(f)[1]
pages[i] = re.FindStringSubmatch(f)[1]
}
TMPL = make(map[string][]byte, len(files))
for i, f := range files {
@ -23,9 +23,9 @@ func generateTmpl() {
t, _ := template.ParseFiles(f)
t.ParseGlob("tmpl/*.tmpl")
t.Execute(b, map[string]any{
"name": names[i],
"names": names,
"page": pages[i],
"pages": pages,
})
TMPL[names[i]] = b.Bytes()
TMPL[pages[i]] = b.Bytes()
}
}

View File

@ -1,9 +1,8 @@
package main
import (
"path/filepath"
"net/http"
"fmt"
"path/filepath"
)
func index(w http.ResponseWriter, r *http.Request) {
@ -14,13 +13,14 @@ func static(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path)
}
func css(w http.ResponseWriter, r *http.Request) {
fmt.Println(filepath.Join("css", getParam(r, 0)))
http.ServeFile(w, r, filepath.Join("css", getParam(r, 0)))
func file(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filepath.Join(getParam(r, 1), getParam(r, 0)))
}
func html(w http.ResponseWriter, r *http.Request) {
if t, found := TMPL[getParam(r, 0)]; found {
if a, found := ALIASES[getParam(r, 0)]; found {
http.Redirect(w, r, a, http.StatusFound)
} else if t, found := TMPL[getParam(r, 0)]; found {
w.Write(t)
} else {
http.NotFound(w, r)

View File

@ -1,3 +1,3 @@
<meta charset="UTF-8">
<title>Ange DUHAYON</title>
<title>Ange DUHAYON - {{.page}}</title>
<link rel="stylesheet" href="style.css"/>

View File

@ -4,9 +4,9 @@
<span id="credits" class="right" style="font-size: .4em;">Website heavily inspired from <a href="//suckless.org/" target="_blank" rel="noreferrer noopener">suckless.org</a></span>
</div>
<div id="menu">
{{$name := .name}}
{{range .names}}
<a href="/{{.}}">{{if eq $name .}}<b>{{.}}</b>{{else}}{{.}}{{end}}</a>
{{$page := .page}}
{{range .pages}}
<a href="/{{.}}">{{if eq $page .}}<b>{{.}}</b>{{else}}{{.}}{{end}}</a>
{{end}}
<span class="right">
<a href="//git.gmoker.com/yw5n" target="_blank" rel="noreferrer noopener">source</a>

31
tmpl/vars.tmpl Normal file
View File

@ -0,0 +1,31 @@
{{define "matrix"}}
<a href="//app.element.io/#/user/@ange:gmoker.com" target="_blank" rel="noreferrer noopener">@ange:gmoker.com</a>
{{end}}
{{define "discord"}}
@elrilio
{{end}}
{{define "phone"}}
<a href="tel:+33582951623" target="_blank" rel="noreferrer noopener">+33 5 82 95 16 23</a>
{{end}}
{{define "email"}}
<a href="mailto:ange@yw5n.com" target="_blank" rel="noreferrer noopener">ange@yw5n.com</a>
{{end}}
{{define "wa"}}
<a href="//wa.me/6285333559453" target="_blank" rel="noreferrer noopener">+62 853-3355-9453</a>
{{end}}
{{define "in"}}
<a href="//www.linkedin.com/in/angedhn" target="_blank" rel="noreferrer noopener">angedhn</a>
{{end}}
{{define "pgp"}}
<a href="/static/pgp.asc" target="_blank" rel="noreferrer noopener">pgp.asc</a>
{{end}}
{{define "ssh"}}
<a href="/static/ssh" target="_blank" rel="noreferrer noopener">ssh</a>
{{end}}