From accc7d850319324336836b62bd4309469c0d26f1 Mon Sep 17 00:00:00 2001 From: herkulessi Date: Sat, 4 Jul 2026 20:39:51 +0200 Subject: Initial Commit --- genericerror.html | 16 +++++++ go.mod | 3 ++ main.go | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ notfound.html | 16 +++++++ popup.html | 23 +++++++++ 5 files changed, 194 insertions(+) create mode 100644 genericerror.html create mode 100644 go.mod create mode 100644 main.go create mode 100644 notfound.html create mode 100644 popup.html diff --git a/genericerror.html b/genericerror.html new file mode 100644 index 0000000..304d3b8 --- /dev/null +++ b/genericerror.html @@ -0,0 +1,16 @@ + + + + + + + Stuve IT Error + + + +
+

Etwas ist schiefgelaufen / Something went wrong!

+ If you believe this to be a mistake or the problem persists, please contact stuve-it@fau.de +
+ + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0b88743 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module herkulessi.de/git/mvpad + +go 1.26.4 diff --git a/main.go b/main.go new file mode 100644 index 0000000..cd8c20f --- /dev/null +++ b/main.go @@ -0,0 +1,136 @@ +package main + +import ( + _ "embed" + "encoding/gob" + "io" + "log/slog" + "net/http" + "net/url" + "os" + "strings" + "sync" +) + +//go:embed popup.html +var popup string + +//go:embed notfound.html +var notfound string + +//go:embed genericerror.html +var genericerror string + +var migratedLock = sync.RWMutex{} +var migrated = map[string]struct{}{} + +func migrate(id string) { + migratedLock.Lock() + migrated[id] = struct{}{} + migratedLock.Unlock() + slog.Info("Saving migrations", "id", id) + save() +} +func save() { + file, err := os.Create("migrated") + if err != nil { + slog.Warn("Could not create Migration-File", "err", err) + return + } + defer file.Close() + enc := gob.NewEncoder(file) + migratedLock.RLock() + defer migratedLock.RUnlock() + err = enc.Encode(migrated) + if err != nil { + slog.Warn("Could not encode migrations", "err", err) + return + } +} + +func main() { + file, err := os.Open("migrated") + if err != nil { + slog.Warn("Could not open migration history", "err", err) + save() + } else { + dec := gob.NewDecoder(file) + dec.Decode(&migrated) + } + + http.HandleFunc("GET /p/{id}", func(w http.ResponseWriter, r *http.Request) { + id := url.PathEscape(r.PathValue("id")) + + migratedLock.RLock() + if _, ok := migrated[id]; ok { + migratedLock.RUnlock() + http.Redirect(w, r, "https://notes.stuve.fau.de/stuve-etherpad-migrated-"+id, 302) + return + } + migratedLock.RUnlock() + + req, err := http.Get("http://127.0.0.1:9001/p/" + id + "/export/html") + if err != nil { + w.WriteHeader(502) + slog.Error("Could not fetch Pad as html", "err", err, "id", id) + w.Write([]byte(genericerror)) + return + } + defer req.Body.Close() + if req.StatusCode == 404 { + w.WriteHeader(404) + slog.Error("Could not fetch Pad: 404", "id", id) + w.Write([]byte(notfound)) + return + } + body, err := io.ReadAll(req.Body) + if req.StatusCode != 200 { + w.WriteHeader(req.StatusCode) + slog.Error("Could not fetch Pad", "id", id, "status", req.Status, "body", string(body)) + w.Write([]byte(genericerror)) + return + } + if err != nil { + w.WriteHeader(502) + slog.Error("Could not read entire body", "err", err, "id", id) + w.Write([]byte(genericerror)) + return + } + w.Write([]byte(strings.Replace(string(body), "", popup, 1))) + }) + http.HandleFunc("POST /p/{id}", func(w http.ResponseWriter, r *http.Request) { + id := r.PathValue("id") + req, err := http.Get("http://127.0.0.1:9001/p/" + id + "/export/markdown") + if err != nil { + w.WriteHeader(502) + slog.Error("Could not fetch Pad as markdown", "err", err, "id", id) + w.Write([]byte(genericerror)) + return + } + if req.StatusCode == 404 { + w.WriteHeader(404) + slog.Error("Could not fetch Pad: 404", "id", id) + w.Write([]byte(notfound)) + return + } + if req.StatusCode != 200 { + body, err := io.ReadAll(req.Body) + w.WriteHeader(req.StatusCode) + slog.Error("Could not fetch Pad", "id", id, "status", req.Status, "body", string(body), "err", err) + w.Write([]byte(genericerror)) + return + } + resp, err := http.Post("https://notes.stuve.fau.de/new/stuve-etherpad-migrated-"+id, "text/markdown", req.Body) + if resp.StatusCode != 200 { + body, err := io.ReadAll(resp.Body) + w.WriteHeader(req.StatusCode) + slog.Error("Could not Post Pad", "id", id, "status", req.Status, "body", string(body), "err", err) + w.Write([]byte(genericerror)) + return + } + migrate(id) + http.Redirect(w, r, "https://notes.stuve.fau.de/stuve-etherpad-migrated-"+id, 302) + }) + err = http.ListenAndServe("[::]:8080", nil) + panic(err) +} diff --git a/notfound.html b/notfound.html new file mode 100644 index 0000000..defb72f --- /dev/null +++ b/notfound.html @@ -0,0 +1,16 @@ + + + + + + + Stuve IT Error + + + +
+

Pad existiert nicht! / Pad doesn't exist!

+ If you believe this to be a mistake or the problem persists, please contact stuve-it@fau.de +
+ + diff --git a/popup.html b/popup.html new file mode 100644 index 0000000..92247d7 --- /dev/null +++ b/popup.html @@ -0,0 +1,23 @@ + + +
+

Schreibgeschützte Kopie des Pads / Readonly Copy of this pad
+ Zum Bearbeiten hier zu Stuve Notes umziehen: / To edit this page migrate it to Stuve Notes:

+
+
-- cgit v1.2.3