Compare commits

...

10 commits

Author SHA1 Message Date
aaa16a35ef
fix: point wg.sh to raw script
Some checks failed
/ deploy (push) Failing after 7m7s
2025-10-22 06:52:41 +00:00
efa55bbd03
fix: no nginx ingress 2025-10-22 06:46:05 +00:00
204c4cd4d7
fix: BASE_URL 2025-10-22 06:42:57 +00:00
c247c4ffda
fix: gitea -> forgejo 2025-10-22 06:41:43 +00:00
09dcfdac7a
feat: wg.sh alias 2025-08-17 03:38:47 +00:00
1af1e594ba
fix: copy button broken 2025-07-22 18:18:45 +00:00
177b419463
fix: copy button 2025-07-22 18:18:45 +00:00
1f0c0ba4b1 feat: remove matrix (#7)
Reviewed-on: yw5n/yw5ncom#7
2025-07-22 18:18:32 +00:00
9420df0a4c fix: copy button (#6)
Reviewed-on: yw5n/yw5ncom#6
2025-04-22 02:18:56 +00:00
abd9218561 feat: aliases (#5)
Reviewed-on: yw5n/yw5ncom#5
2025-04-05 10:17:05 +00:00
24 changed files with 284 additions and 232 deletions

View file

@ -8,24 +8,24 @@ jobs:
- name: setup env - name: setup env
run: | run: |
. ./.env || true . ./.env || true
if [ "${{ gitea.ref_name }}" == prod ] && [ -n "$PROD_URL" ]; then if [ "${{ forgejo.ref_name }}" = prod ] && [ -n "$PROD_URL" ]; then
BASE_URL="$PROD_URL" BASE_URL="$PROD_URL"
else else
BASE_URL="${{ gitea.ref_name }}.$(tr / '\n' <<< "${{ gitea.repository }}" | tac | tr '\n' .)k8s.gmoker.com" BASE_URL="${{ forgejo.ref_name }}.$(tr / '\n' <<< "${{ forgejo.repository }}" | tac | tr '\n' .)k3s.gmoker.com"
fi fi
REGISTRY="$(sed 's .*:// ' <<< ${{ gitea.server_url }})" REGISTRY="$(sed 's .*:// ' <<< ${{ forgejo.server_url }})"
cat <<EOF >> .env cat <<EOF >> .env
BASE_URL="$BASE_URL" BASE_URL="$BASE_URL"
REGISTRY="$REGISTRY" REGISTRY="$REGISTRY"
IMAGEAPP="$REGISTRY/${{ gitea.repository }}:${{ gitea.ref_name }}" IMAGEAPP="$REGISTRY/${{ forgejo.repository }}:${{ forgejo.ref_name }}"
EOF EOF
cat .env cat .env
- uses: actions/kaniko@v1 - uses: actions/buildkit@v1
with: with:
password: "${{ secrets.PKGRW }}" password: "${{ secrets.PKGRW }}"
- uses: actions/k8sdeploy@v1 - uses: actions/k8sdeploy@v2
with: with:
kubeconfig: "${{ secrets.K8S }}" kubeconfig: "${{ secrets.K3S }}"
registry_password: "${{ secrets.PKGRW }}" registry_password: "${{ secrets.PKGRW }}"

View file

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

1
README.md Normal file
View file

@ -0,0 +1 @@
# yw5n.com

4
aliases.txt Normal file
View file

@ -0,0 +1,4 @@
termux.sh https://git.gmoker.com/ange/termux/raw/branch/main/install.sh
dotfiles https://git.gmoker.com/ange/dotfiles
arch https://git.gmoker.com/ange/archinstall
wg.sh https://git.gmoker.com/ange/wg/raw/branch/main/install.sh

View file

@ -5,7 +5,15 @@
margin: 5mm; margin: 5mm;
} }
#menu, #credits { #credits, #menu {
display: none; display: none;
} }
#main {
margin: 0;
}
#nav {
float: right;
}
} }

2
go.mod
View file

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

View file

@ -2,68 +2,66 @@
<html> <html>
<head> <head>
{{template "head.tmpl" .}} {{template "head.tmpl" .}}
<script src="static/copy.js"></script> <script>
function copyElem(id, r='') {
navigator.clipboard.writeText(
document.getElementById(id).innerText.replaceAll(r, ''),
);
}
function copyFile(id) {
fetch(document.getElementById(id).childNodes[1].href).then(f => {
f.text().then(t => {
navigator.clipboard.writeText(t);
})
})
}
</script>
</head> </head>
<body> <body>
{{template "header.tmpl" .}} {{template "header.tmpl" .}}
<div id="content"> <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"> <div id="main">
<table> <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><button onclick="copyElem('matrix')">Copy</button></td>
</tr>
<tr> <tr>
<td>Discord</td> <td>Discord</td>
<td id="discord">@elrilio</td> <td id="discord">{{template "discord"}}</td>
<td><button onclick="copyElem('discord')">Copy</button></td> <td><button onclick="copyElem('discord')">Copy</button></td>
</tr> </tr>
<tr> <tr>
<td>Phone (France)</td> <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> <td><button onclick="copyElem('phone', / /g)">Copy</button></td>
</tr> </tr>
<tr> <tr>
<td>Email</td> <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> <td><button onclick="copyElem('email')">Copy</button></td>
</tr> </tr>
<tr> <tr>
<td>WhatsApp</td> <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> <td><button onclick="copyElem('wa', /[ |-]/g)">Copy</button></td>
</tr> </tr>
<tr> <tr>
<td>LinkedIn</td> <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> <td><button onclick="copyElem('in')">Copy</button></td>
</tr> </tr>
<tr> <tr>
<td>PGP</td> <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> <td><button onclick="copyFile('pgp')">Copy</button></td>
</tr> </tr>
<tr> <tr>
<td>SSH</td> <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> <td><button onclick="copyFile('ssh')">Copy</button></td>
</tr> </tr>
</table> </table>
</div>
</div> </div>
</div>
</body> </body>
</html> </html>

View file

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

View file

@ -1,34 +1,36 @@
#!/bin/bash -e #!/bin/bash -e
set -o pipefail set -o pipefail
function kapply() { kapply() {
for f in "$@"; do for f in "$@"; do
kubectl apply -f \ kubectl apply --server-side \
<(envsubst "$(env | xargs printf '$%s ')" < "manifests/$f") -f<(envsubst "$(env | sed 's/^/$/')" < "manifests/$f")
done done
} }; export -f kapply
function kcreatesec() { kcreatesec() {
kubectl create secret generic --save-config --dry-run=client -oyaml "$@" | kubectl apply -f- kubectl apply --server-side \
} -f<(kubectl create secret generic --dry-run=client -oyaml "$@")
}; export -f kcreatesec
function kcreatecm() { kcreatecm() {
kubectl create configmap --dry-run=client -oyaml "$@" | kubectl apply -f- kubectl apply --server-side \
} -f<(kubectl create configmap --dry-run=client -oyaml "$@")
}; export -f kcreatecm
function kgseckey() { kgseckey() {
local sec="$1"; shift local sec="$1"; shift
local key="$1"; shift local key="$1"; shift
kubectl get secret "$sec" -o jsonpath="{.data.$key}" | base64 -d kubectl get secret "$sec" -ojson | jq -re ".data.\"$key\"" | base64 -d
} }; export -f kgseckey
function kgcmkey() { kgcmkey() {
local cm="$1"; shift local cm="$1"; shift
local key="$1"; shift local key="$1"; shift
kubectl get configmap "$cm" -o jsonpath="{.data.$key}" kubectl get configmap "$cm" -ojson | jq -re ".data.\"$key\""
} }; export -f kgcmkey
kapply common/app.yaml kapply common/app.yaml

View file

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

View file

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

View file

@ -6,7 +6,6 @@ metadata:
annotations: annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
spec: spec:
ingressClassName: nginx
tls: tls:
- secretName: tls-app - secretName: tls-app
hosts: hosts:

View file

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

View file

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

View file

@ -1,31 +0,0 @@
package main
import (
"bytes"
"html/template"
"path/filepath"
"regexp"
)
var TMPL map[string][]byte
func generateTmpl() {
files, _ := filepath.Glob("html/*.html")
re := regexp.MustCompile("html/(.+).html")
names := make([]string, len(files))
for i, f := range files {
names[i] = re.FindStringSubmatch(f)[1]
}
TMPL = make(map[string][]byte, len(files))
for i, f := range files {
b := new(bytes.Buffer)
t, _ := template.ParseFiles(f)
t.ParseGlob("tmpl/*.tmpl")
t.Execute(b, map[string]any{
"name": names[i],
"names": names,
})
TMPL[names[i]] = b.Bytes()
}
}

50
src/util.go Normal file
View file

@ -0,0 +1,50 @@
package main
import (
"bytes"
"html/template"
"log"
"os"
"path/filepath"
"regexp"
"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]
}
}
}
var TMPL map[string][]byte
func generateTmpl() {
files, _ := filepath.Glob("html/*.html")
re := regexp.MustCompile("html/(.+).html")
pages := make([]string, len(files))
for i, f := range files {
pages[i] = re.FindStringSubmatch(f)[1]
}
TMPL = make(map[string][]byte, len(files))
for i, f := range files {
b := new(bytes.Buffer)
t, _ := template.ParseFiles(f)
t.ParseGlob("tmpl/*.tmpl")
t.Execute(b, map[string]any{
"page": pages[i],
"pages": pages,
})
TMPL[pages[i]] = b.Bytes()
}
}

View file

@ -1,9 +1,8 @@
package main package main
import ( import (
"path/filepath"
"net/http" "net/http"
"fmt" "path/filepath"
) )
func index(w http.ResponseWriter, r *http.Request) { func index(w http.ResponseWriter, r *http.Request) {
@ -15,12 +14,13 @@ func static(w http.ResponseWriter, r *http.Request) {
} }
func css(w http.ResponseWriter, r *http.Request) { 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))) http.ServeFile(w, r, filepath.Join("css", getParam(r, 0)))
} }
func html(w http.ResponseWriter, r *http.Request) { 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) w.Write(t)
} else { } else {
http.NotFound(w, r) http.NotFound(w, r)

View file

@ -1 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDUccdlgUHzV+AhWDyjwcG4QwSNbybIV8MF7c6XpKQl4 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDUccdlgUHzV+AhWDyjwcG4QwSNbybIV8MF7c6XpKQl4 ange@yw5n.com

View file

@ -1,13 +0,0 @@
function copyElem(id, r='') {
navigator.clipboard.writeText(
document.getElementById(id).innerText.replaceAll(r, ''),
);
}
function copyFile(id) {
fetch(document.getElementById(id).childNodes[0].href).then(f => {
f.text().then(t => {
navigator.clipboard.writeText(t);
})
})
}

View file

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

View file

@ -1,14 +1,14 @@
<div id="header"> <div id="header">
<a id="headerLink" href="/">Ange DUHAYON</a> <a id="headerLink" href="/">Ange DUHAYON</a>
<span id="headerSubtitle">DevOps Engineer</span> <span id="headerSubtitle">DevOps Engineer</span>
<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> <span id="credits" class="right" style="font-size: .4em;">Website heavily inspired from <a href="https://suckless.org/" target="_blank" rel="noreferrer noopener">suckless.org</a></span>
</div> </div>
<div id="menu"> <div id="menu">
{{$name := .name}} {{$page := .page}}
{{range .names}} {{range .pages}}
<a href="/{{.}}">{{if eq $name .}}<b>{{.}}</b>{{else}}{{.}}{{end}}</a> <a href="/{{.}}">{{if eq $page .}}<b>{{.}}</b>{{else}}{{.}}{{end}}</a>
{{end}} {{end}}
<span class="right"> <span class="right">
<a href="//git.gmoker.com/yw5n" target="_blank" rel="noreferrer noopener">source</a> <a href="https://git.gmoker.com/yw5n" target="_blank" rel="noreferrer noopener">source</a>
</span> </span>
</div> </div>

27
tmpl/vars.tmpl Normal file
View file

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