Compare commits
No commits in common. "c4e53afdf11c6671e285349f7b06a8452a038e0c" and "9d0da00371d8e542e7ee7389019d5e0d2f113859" have entirely different histories.
c4e53afdf1
...
9d0da00371
10
Dockerfile
10
Dockerfile
@ -6,10 +6,8 @@ COPY src/ .
|
|||||||
RUN CGO_ENABLED=0 go build -o /app
|
RUN CGO_ENABLED=0 go build -o /app
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=build /app .
|
COPY --from=build /app /app
|
||||||
COPY html/ html/
|
COPY static/ /static/
|
||||||
COPY css/ css/
|
COPY html/ /html/
|
||||||
COPY static/ static/
|
|
||||||
COPY tmpl/ tmpl/
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD ["./app"]
|
CMD ["/app"]
|
||||||
|
10
compose.yaml
10
compose.yaml
@ -8,15 +8,9 @@ services:
|
|||||||
watch:
|
watch:
|
||||||
- action: rebuild
|
- action: rebuild
|
||||||
path: src/
|
path: src/
|
||||||
- action: sync+restart
|
|
||||||
path: tmpl/
|
|
||||||
target: tmpl/
|
|
||||||
- action: sync+restart
|
- action: sync+restart
|
||||||
path: html/
|
path: html/
|
||||||
target: html/
|
target: html
|
||||||
- action: sync
|
|
||||||
path: css/
|
|
||||||
target: css/
|
|
||||||
- action: sync
|
- action: sync
|
||||||
path: static/
|
path: static/
|
||||||
target: static/
|
target: static
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
@media print {
|
|
||||||
@page {
|
|
||||||
size: A4;
|
|
||||||
size: portrait;
|
|
||||||
margin: 5mm;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu, #credits {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
33
html/about.html
Normal file
33
html/about.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Ange DUHAYON</title>
|
||||||
|
<link rel="stylesheet" href="style.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="header">
|
||||||
|
<a id="headerLink" href="/">Ange DUHAYON</a>
|
||||||
|
<span id="headerSubtitle">DevOps Engineer</span>
|
||||||
|
<span 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">
|
||||||
|
<a href="/">contact</a>
|
||||||
|
<a href="/resume">resume</a>
|
||||||
|
<a href="/about"><b>about me</b></a>
|
||||||
|
<span class="right">
|
||||||
|
<a href="//git.gmoker.com/yw5n/" target="_blank" rel="noreferrer noopener">source</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="content">
|
||||||
|
<!--
|
||||||
|
<div id="nav">
|
||||||
|
<ul>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<div id="main">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,11 +1,41 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head.tmpl" .}}
|
<meta charset="UTF-8">
|
||||||
<script src="static/copy.js"></script>
|
<title>Ange DUHAYON</title>
|
||||||
|
<link rel="stylesheet" href="style.css"/>
|
||||||
|
<script>
|
||||||
|
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);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{template "header.tmpl" .}}
|
<div id="header">
|
||||||
|
<a id="headerLink" href="/">Ange DUHAYON</a>
|
||||||
|
<span id="headerSubtitle">DevOps Engineer</span>
|
||||||
|
<span 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">
|
||||||
|
<a href="/"><b>contact</b></a>
|
||||||
|
<a href="/resume">resume</a>
|
||||||
|
<!--
|
||||||
|
<a href="/about">about me</a>
|
||||||
|
-->
|
||||||
|
<span class="right">
|
||||||
|
<a href="//git.gmoker.com/yw5n/" target="_blank" rel="noreferrer noopener">source</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<!--
|
<!--
|
||||||
<div id="nav">
|
<div id="nav">
|
||||||
@ -46,11 +76,6 @@
|
|||||||
<td id="wa"><a href="//wa.me/6285333559453" target="_blank" rel="noreferrer noopener">+62 853-3355-9453</a></td>
|
<td id="wa"><a href="//wa.me/6285333559453" target="_blank" rel="noreferrer noopener">+62 853-3355-9453</a></td>
|
||||||
<td><button onclick="copyElem('wa', /[ |-]/g)">Copy</button></td>
|
<td><button onclick="copyElem('wa', /[ |-]/g)">Copy</button></td>
|
||||||
</tr>
|
</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><button onclick="copyElem('in')">Copy</button></td>
|
|
||||||
</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"><a href="/static/pgp.asc" target="_blank" rel="noreferrer noopener">pgp.asc</a></td>
|
@ -1,11 +1,26 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head.tmpl" .}}
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="resume.css"/>
|
<title>Ange DUHAYON</title>
|
||||||
|
<link rel="stylesheet" href="style.css"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{template "header.tmpl" .}}
|
<div id="header">
|
||||||
|
<a id="headerLink" href="/">Ange DUHAYON</a>
|
||||||
|
<span id="headerSubtitle">DevOps Engineer</span>
|
||||||
|
<span 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">
|
||||||
|
<a href="/">contact</a>
|
||||||
|
<a href="/resume"><b>resume</b></a>
|
||||||
|
<!--
|
||||||
|
<a href="/about">about me</a>
|
||||||
|
-->
|
||||||
|
<span class="right">
|
||||||
|
<a href="//git.gmoker.com/yw5n/" target="_blank" rel="noreferrer noopener">source</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div id="nav">
|
<div id="nav">
|
||||||
<!--
|
<!--
|
||||||
@ -31,21 +46,20 @@
|
|||||||
<h3><b>Freelance</b> - DevOps</h3>
|
<h3><b>Freelance</b> - DevOps</h3>
|
||||||
SEPTEMBER 2023 - TODAY
|
SEPTEMBER 2023 - TODAY
|
||||||
<ul>
|
<ul>
|
||||||
<li>Server setup (Debian, Docker/K8S, web server, customer application)</li>
|
<li>Server setup (Debian, Docker/K8s, web server, customer application)</li>
|
||||||
<li>CI/CD, real-time monitoring</li>
|
<li>CI/CD, real-time monitoring</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3><b>Selfhost</b> - DevOps</h3>
|
<h3><b>Selfhost</b> - DevOps</h3>
|
||||||
JANUARY 2022 - TODAY
|
JANUARY 2022 - TODAY
|
||||||
<ul>
|
<ul>
|
||||||
<li>Debian, K8S, Gitea CI/CD</li>
|
<li>Debian + K8S + Gitea CI/CD + 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> - Cobra</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>
|
||||||
</ul>
|
</ul>
|
||||||
<h3><b>Predicloud, Toulouse</b> - DevOps SRE</h3>
|
<h3><b>Predicloud, Toulouse</b> - DevOps / Site Reliability Manager</h3>
|
||||||
JUNE 2022 - JULY 2023
|
JUNE 2022 - JULY 2023
|
||||||
<ul>
|
<ul>
|
||||||
<li>CI/CD, K8S monitoring</li>
|
<li>CI/CD, K8S monitoring</li>
|
||||||
@ -61,7 +75,7 @@
|
|||||||
<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>
|
||||||
<tr><td>Go</td> <td>Docker</td></tr>
|
<tr><td>Go</td> <td>Docker</td></tr>
|
||||||
<tr><td>JS</td> <td>K8S</td></tr>
|
<tr><td>JavaScript</td><td>K8S</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,8 +7,9 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
http.HandleFunc("/", route)
|
http.HandleFunc("/", route)
|
||||||
generateTmpl()
|
|
||||||
if err := http.ListenAndServe(":3000", nil); err != nil {
|
err := http.ListenAndServe(":3000", nil)
|
||||||
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/route.go
11
src/route.go
@ -16,19 +16,15 @@ var routes = []struct {
|
|||||||
handler http.HandlerFunc
|
handler http.HandlerFunc
|
||||||
}{
|
}{
|
||||||
{[]string{"GET"}, url(""), index},
|
{[]string{"GET"}, url(""), index},
|
||||||
|
{[]string{"GET"}, url("/style\\.css"), style},
|
||||||
{[]string{"GET"}, url("/static/.+"), static},
|
{[]string{"GET"}, url("/static/.+"), static},
|
||||||
{[]string{"GET"}, url("/(.+\\.css)"), css},
|
{[]string{"GET"}, url("/[^/]+"), html},
|
||||||
{[]string{"GET"}, url("/([^/]+)"), html},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func url(s string) *regexp.Regexp {
|
func url(s string) *regexp.Regexp {
|
||||||
return regexp.MustCompile("^" + s + "/?$")
|
return regexp.MustCompile("^" + s + "/?$")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getParam(r *http.Request, i int) string {
|
|
||||||
return r.Context().Value(URLParam{}).([]string)[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func route(w http.ResponseWriter, r *http.Request) {
|
func route(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, rt := range routes {
|
for _, rt := range routes {
|
||||||
matches := rt.regex.FindStringSubmatch(r.URL.Path)
|
matches := rt.regex.FindStringSubmatch(r.URL.Path)
|
||||||
@ -38,9 +34,8 @@ func route(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(
|
http.Error(
|
||||||
w, "405 method not allowed", http.StatusMethodNotAllowed,
|
w, "405 method not allowed", http.StatusMethodNotAllowed,
|
||||||
)
|
)
|
||||||
return
|
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:])),
|
||||||
)
|
)
|
||||||
|
31
src/tmpl.go
31
src/tmpl.go
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
20
src/views.go
20
src/views.go
@ -1,28 +1,22 @@
|
|||||||
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) {
|
||||||
http.Redirect(w, r, "/contact", http.StatusMovedPermanently)
|
http.ServeFile(w, r, filepath.Join("html", "index.html"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func style(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(w, r, "/html/style.css")
|
||||||
}
|
}
|
||||||
|
|
||||||
func static(w http.ResponseWriter, r *http.Request) {
|
func static(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFile(w, r, r.URL.Path)
|
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 html(w http.ResponseWriter, r *http.Request) {
|
func html(w http.ResponseWriter, r *http.Request) {
|
||||||
if t, found := TMPL[getParam(r, 0)]; found {
|
http.ServeFile(w, r, filepath.Join("html", r.URL.Path + ".html"))
|
||||||
w.Write(t)
|
|
||||||
} else {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
<meta charset="UTF-8">
|
|
||||||
<title>Ange DUHAYON</title>
|
|
||||||
<link rel="stylesheet" href="style.css"/>
|
|
@ -1,14 +0,0 @@
|
|||||||
<div id="header">
|
|
||||||
<a id="headerLink" href="/">Ange DUHAYON</a>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
<div id="menu">
|
|
||||||
{{$name := .name}}
|
|
||||||
{{range .names}}
|
|
||||||
<a href="/{{.}}">{{if eq $name .}}<b>{{.}}</b>{{else}}{{.}}{{end}}</a>
|
|
||||||
{{end}}
|
|
||||||
<span class="right">
|
|
||||||
<a href="//git.gmoker.com/yw5n" target="_blank" rel="noreferrer noopener">source</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user