From 57c4d9757ae09700e6bd65de6e55f5cd17e6e6e2 Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Sun, 6 Oct 2024 08:53:06 -0400 Subject: move some old code to the _old folder to save CI time Signed-off-by: Xe Iaso --- Earthfile | 11 - cmd/_old/README.md | 3 + cmd/_old/alvis/fly.go | 16 + cmd/_old/alvis/incidents/.gitignore | 1 + cmd/_old/alvis/main.go | 427 ++++++ cmd/_old/alvis/playbooks/README.md | 18 + .../alvis/playbooks/xe-pronouns-healthcheck.json | 9 + cmd/_old/alvis/prompts.go | 77 + cmd/_old/apeirophobia/.gitignore | 1 + cmd/_old/apeirophobia/main.go | 168 +++ cmd/_old/apeirophobia/messages.go | 20 + cmd/_old/apeirophobia/schema.sql | 7 + cmd/_old/apeirophobia/static/robots.txt | 2 + cmd/_old/apeirophobia/tmpl/404.tmpl | 5 + cmd/_old/apeirophobia/tmpl/base.tmpl | 25 + cmd/_old/apeirophobia/tmpl/error.tmpl | 7 + cmd/_old/apeirophobia/tmpl/index.tmpl | 11 + cmd/_old/azurda/README.md | 23 + cmd/_old/azurda/fly.toml | 27 + cmd/_old/azurda/index.templ | 47 + cmd/_old/azurda/index_templ.go | 87 ++ cmd/_old/azurda/main.go | 286 ++++ cmd/_old/azurda/stablediffusion.go | 161 ++ cmd/_old/azurda/static/img/azurda-screenshot.png | Bin 0 -> 997615 bytes cmd/_old/azurda/static/img/azurda.png | Bin 0 -> 69760 bytes .../static/img/high-level-overview.excalidraw.svg | 21 + cmd/_old/azurda/static/js/alpine.js | 9 + cmd/_old/azurda/static/js/md5.min.js | 2 + cmd/_old/azurda/yeetfile.js | 4 + cmd/_old/cadeybot/.gitignore | 3 + cmd/_old/cadeybot/corpusmake.sh | 9 + cmd/_old/cadeybot/importer/main.go | 42 + cmd/_old/cadeybot/main.go | 104 ++ cmd/_old/cadeybot/markov.go | 137 ++ cmd/_old/cadeybot/znc/znc.go | 63 + cmd/_old/geminitest/main.go | 40 + cmd/_old/hnscrape/hn.go | 251 ++++ cmd/_old/hnscrape/main.go | 259 ++++ cmd/_old/hnscrape/var/.gitignore | 2 + cmd/_old/johaus/.gitignore | 2 + cmd/_old/johaus/main.go | 95 ++ cmd/_old/johaus/yeetfile.js | 3 + cmd/_old/tourian/.gitignore | 1 + cmd/_old/tourian/ent/chatmessage.go | 123 ++ cmd/_old/tourian/ent/chatmessage/chatmessage.go | 74 + cmd/_old/tourian/ent/chatmessage/where.go | 288 ++++ cmd/_old/tourian/ent/chatmessage_create.go | 240 +++ cmd/_old/tourian/ent/chatmessage_delete.go | 88 ++ cmd/_old/tourian/ent/chatmessage_query.go | 526 +++++++ cmd/_old/tourian/ent/chatmessage_update.go | 323 ++++ cmd/_old/tourian/ent/client.go | 483 ++++++ cmd/_old/tourian/ent/conversation.go | 101 ++ cmd/_old/tourian/ent/conversation/conversation.go | 54 + cmd/_old/tourian/ent/conversation/where.go | 148 ++ cmd/_old/tourian/ent/conversation_create.go | 204 +++ cmd/_old/tourian/ent/conversation_delete.go | 88 ++ cmd/_old/tourian/ent/conversation_query.go | 526 +++++++ cmd/_old/tourian/ent/conversation_update.go | 235 +++ cmd/_old/tourian/ent/ent.go | 610 ++++++++ cmd/_old/tourian/ent/enttest/enttest.go | 84 ++ cmd/_old/tourian/ent/generate.go | 3 + cmd/_old/tourian/ent/hook/hook.go | 211 +++ cmd/_old/tourian/ent/migrate/migrate.go | 64 + cmd/_old/tourian/ent/migrate/schema.go | 43 + cmd/_old/tourian/ent/mutation.go | 801 ++++++++++ cmd/_old/tourian/ent/predicate/predicate.go | 13 + cmd/_old/tourian/ent/runtime.go | 43 + cmd/_old/tourian/ent/runtime/runtime.go | 10 + cmd/_old/tourian/ent/schema/chatmessage.go | 26 + cmd/_old/tourian/ent/schema/conversation.go | 24 + cmd/_old/tourian/ent/tx.go | 213 +++ cmd/_old/tourian/fly.toml | 31 + cmd/_old/tourian/main.go | 289 ++++ cmd/_old/tourian/package-lock.json | 1554 ++++++++++++++++++++ cmd/_old/tourian/package.json | 22 + cmd/_old/tourian/static/font/podkova.css | 7 + cmd/_old/tourian/static/font/podkova.woff2 | Bin 0 -> 60580 bytes cmd/_old/tourian/static/js/htmx.min.js | 1 + cmd/_old/tourian/static/js/ws.js | 476 ++++++ cmd/_old/tourian/static/styles.css | 1 + cmd/_old/tourian/tailwind.config.js | 14 + cmd/_old/tourian/tourian.templ | 303 ++++ cmd/_old/tourian/tourian_templ.go | 394 +++++ cmd/_old/tourian/yeetfile.js | 4 + cmd/alvis/fly.go | 16 - cmd/alvis/incidents/.gitignore | 1 - cmd/alvis/main.go | 427 ------ cmd/alvis/playbooks/README.md | 18 - cmd/alvis/playbooks/xe-pronouns-healthcheck.json | 9 - cmd/alvis/prompts.go | 77 - cmd/apeirophobia/.gitignore | 1 - cmd/apeirophobia/main.go | 168 --- cmd/apeirophobia/messages.go | 20 - cmd/apeirophobia/schema.sql | 7 - cmd/apeirophobia/static/robots.txt | 2 - cmd/apeirophobia/tmpl/404.tmpl | 5 - cmd/apeirophobia/tmpl/base.tmpl | 25 - cmd/apeirophobia/tmpl/error.tmpl | 7 - cmd/apeirophobia/tmpl/index.tmpl | 11 - cmd/azurda/README.md | 23 - cmd/azurda/fly.toml | 27 - cmd/azurda/index.templ | 47 - cmd/azurda/index_templ.go | 87 -- cmd/azurda/main.go | 286 ---- cmd/azurda/stablediffusion.go | 161 -- cmd/azurda/static/img/azurda-screenshot.png | Bin 997615 -> 0 bytes cmd/azurda/static/img/azurda.png | Bin 69760 -> 0 bytes .../static/img/high-level-overview.excalidraw.svg | 21 - cmd/azurda/static/js/alpine.js | 9 - cmd/azurda/static/js/md5.min.js | 2 - cmd/azurda/yeetfile.js | 4 - cmd/cadeybot/.gitignore | 3 - cmd/cadeybot/corpusmake.sh | 9 - cmd/cadeybot/importer/main.go | 42 - cmd/cadeybot/main.go | 104 -- cmd/cadeybot/markov.go | 137 -- cmd/cadeybot/znc/znc.go | 63 - cmd/geminitest/main.go | 40 - cmd/hnscrape/hn.go | 251 ---- cmd/hnscrape/main.go | 259 ---- cmd/hnscrape/var/.gitignore | 2 - cmd/johaus/.gitignore | 2 - cmd/johaus/main.go | 95 -- cmd/johaus/yeetfile.js | 3 - cmd/mimi/manifest/deployment.yaml | 5 + cmd/tourian/.gitignore | 1 - cmd/tourian/ent/chatmessage.go | 123 -- cmd/tourian/ent/chatmessage/chatmessage.go | 74 - cmd/tourian/ent/chatmessage/where.go | 288 ---- cmd/tourian/ent/chatmessage_create.go | 240 --- cmd/tourian/ent/chatmessage_delete.go | 88 -- cmd/tourian/ent/chatmessage_query.go | 526 ------- cmd/tourian/ent/chatmessage_update.go | 323 ---- cmd/tourian/ent/client.go | 483 ------ cmd/tourian/ent/conversation.go | 101 -- cmd/tourian/ent/conversation/conversation.go | 54 - cmd/tourian/ent/conversation/where.go | 148 -- cmd/tourian/ent/conversation_create.go | 204 --- cmd/tourian/ent/conversation_delete.go | 88 -- cmd/tourian/ent/conversation_query.go | 526 ------- cmd/tourian/ent/conversation_update.go | 235 --- cmd/tourian/ent/ent.go | 610 -------- cmd/tourian/ent/enttest/enttest.go | 84 -- cmd/tourian/ent/generate.go | 3 - cmd/tourian/ent/hook/hook.go | 211 --- cmd/tourian/ent/migrate/migrate.go | 64 - cmd/tourian/ent/migrate/schema.go | 43 - cmd/tourian/ent/mutation.go | 801 ---------- cmd/tourian/ent/predicate/predicate.go | 13 - cmd/tourian/ent/runtime.go | 43 - cmd/tourian/ent/runtime/runtime.go | 10 - cmd/tourian/ent/schema/chatmessage.go | 26 - cmd/tourian/ent/schema/conversation.go | 24 - cmd/tourian/ent/tx.go | 213 --- cmd/tourian/fly.toml | 31 - cmd/tourian/main.go | 289 ---- cmd/tourian/package-lock.json | 1554 -------------------- cmd/tourian/package.json | 22 - cmd/tourian/static/font/podkova.css | 7 - cmd/tourian/static/font/podkova.woff2 | Bin 60580 -> 0 bytes cmd/tourian/static/js/htmx.min.js | 1 - cmd/tourian/static/js/ws.js | 476 ------ cmd/tourian/static/styles.css | 1 - cmd/tourian/tailwind.config.js | 14 - cmd/tourian/tourian.templ | 303 ---- cmd/tourian/tourian_templ.go | 394 ----- cmd/tourian/yeetfile.js | 4 - kube/alrest/x/mimi/falin/deployment.yaml | 23 + migroserbices/falin/package-lock.json | 487 +----- 169 files changed, 11254 insertions(+), 11703 deletions(-) create mode 100644 cmd/_old/README.md create mode 100644 cmd/_old/alvis/fly.go create mode 100644 cmd/_old/alvis/incidents/.gitignore create mode 100644 cmd/_old/alvis/main.go create mode 100644 cmd/_old/alvis/playbooks/README.md create mode 100644 cmd/_old/alvis/playbooks/xe-pronouns-healthcheck.json create mode 100644 cmd/_old/alvis/prompts.go create mode 100644 cmd/_old/apeirophobia/.gitignore create mode 100644 cmd/_old/apeirophobia/main.go create mode 100644 cmd/_old/apeirophobia/messages.go create mode 100644 cmd/_old/apeirophobia/schema.sql create mode 100644 cmd/_old/apeirophobia/static/robots.txt create mode 100644 cmd/_old/apeirophobia/tmpl/404.tmpl create mode 100644 cmd/_old/apeirophobia/tmpl/base.tmpl create mode 100644 cmd/_old/apeirophobia/tmpl/error.tmpl create mode 100644 cmd/_old/apeirophobia/tmpl/index.tmpl create mode 100644 cmd/_old/azurda/README.md create mode 100644 cmd/_old/azurda/fly.toml create mode 100644 cmd/_old/azurda/index.templ create mode 100644 cmd/_old/azurda/index_templ.go create mode 100644 cmd/_old/azurda/main.go create mode 100644 cmd/_old/azurda/stablediffusion.go create mode 100644 cmd/_old/azurda/static/img/azurda-screenshot.png create mode 100644 cmd/_old/azurda/static/img/azurda.png create mode 100644 cmd/_old/azurda/static/img/high-level-overview.excalidraw.svg create mode 100644 cmd/_old/azurda/static/js/alpine.js create mode 100644 cmd/_old/azurda/static/js/md5.min.js create mode 100644 cmd/_old/azurda/yeetfile.js create mode 100644 cmd/_old/cadeybot/.gitignore create mode 100755 cmd/_old/cadeybot/corpusmake.sh create mode 100644 cmd/_old/cadeybot/importer/main.go create mode 100644 cmd/_old/cadeybot/main.go create mode 100644 cmd/_old/cadeybot/markov.go create mode 100644 cmd/_old/cadeybot/znc/znc.go create mode 100644 cmd/_old/geminitest/main.go create mode 100644 cmd/_old/hnscrape/hn.go create mode 100644 cmd/_old/hnscrape/main.go create mode 100644 cmd/_old/hnscrape/var/.gitignore create mode 100644 cmd/_old/johaus/.gitignore create mode 100644 cmd/_old/johaus/main.go create mode 100644 cmd/_old/johaus/yeetfile.js create mode 100644 cmd/_old/tourian/.gitignore create mode 100644 cmd/_old/tourian/ent/chatmessage.go create mode 100644 cmd/_old/tourian/ent/chatmessage/chatmessage.go create mode 100644 cmd/_old/tourian/ent/chatmessage/where.go create mode 100644 cmd/_old/tourian/ent/chatmessage_create.go create mode 100644 cmd/_old/tourian/ent/chatmessage_delete.go create mode 100644 cmd/_old/tourian/ent/chatmessage_query.go create mode 100644 cmd/_old/tourian/ent/chatmessage_update.go create mode 100644 cmd/_old/tourian/ent/client.go create mode 100644 cmd/_old/tourian/ent/conversation.go create mode 100644 cmd/_old/tourian/ent/conversation/conversation.go create mode 100644 cmd/_old/tourian/ent/conversation/where.go create mode 100644 cmd/_old/tourian/ent/conversation_create.go create mode 100644 cmd/_old/tourian/ent/conversation_delete.go create mode 100644 cmd/_old/tourian/ent/conversation_query.go create mode 100644 cmd/_old/tourian/ent/conversation_update.go create mode 100644 cmd/_old/tourian/ent/ent.go create mode 100644 cmd/_old/tourian/ent/enttest/enttest.go create mode 100644 cmd/_old/tourian/ent/generate.go create mode 100644 cmd/_old/tourian/ent/hook/hook.go create mode 100644 cmd/_old/tourian/ent/migrate/migrate.go create mode 100644 cmd/_old/tourian/ent/migrate/schema.go create mode 100644 cmd/_old/tourian/ent/mutation.go create mode 100644 cmd/_old/tourian/ent/predicate/predicate.go create mode 100644 cmd/_old/tourian/ent/runtime.go create mode 100644 cmd/_old/tourian/ent/runtime/runtime.go create mode 100644 cmd/_old/tourian/ent/schema/chatmessage.go create mode 100644 cmd/_old/tourian/ent/schema/conversation.go create mode 100644 cmd/_old/tourian/ent/tx.go create mode 100644 cmd/_old/tourian/fly.toml create mode 100644 cmd/_old/tourian/main.go create mode 100644 cmd/_old/tourian/package-lock.json create mode 100644 cmd/_old/tourian/package.json create mode 100644 cmd/_old/tourian/static/font/podkova.css create mode 100644 cmd/_old/tourian/static/font/podkova.woff2 create mode 100644 cmd/_old/tourian/static/js/htmx.min.js create mode 100644 cmd/_old/tourian/static/js/ws.js create mode 100644 cmd/_old/tourian/static/styles.css create mode 100644 cmd/_old/tourian/tailwind.config.js create mode 100644 cmd/_old/tourian/tourian.templ create mode 100644 cmd/_old/tourian/tourian_templ.go create mode 100644 cmd/_old/tourian/yeetfile.js delete mode 100644 cmd/alvis/fly.go delete mode 100644 cmd/alvis/incidents/.gitignore delete mode 100644 cmd/alvis/main.go delete mode 100644 cmd/alvis/playbooks/README.md delete mode 100644 cmd/alvis/playbooks/xe-pronouns-healthcheck.json delete mode 100644 cmd/alvis/prompts.go delete mode 100644 cmd/apeirophobia/.gitignore delete mode 100644 cmd/apeirophobia/main.go delete mode 100644 cmd/apeirophobia/messages.go delete mode 100644 cmd/apeirophobia/schema.sql delete mode 100644 cmd/apeirophobia/static/robots.txt delete mode 100644 cmd/apeirophobia/tmpl/404.tmpl delete mode 100644 cmd/apeirophobia/tmpl/base.tmpl delete mode 100644 cmd/apeirophobia/tmpl/error.tmpl delete mode 100644 cmd/apeirophobia/tmpl/index.tmpl delete mode 100644 cmd/azurda/README.md delete mode 100644 cmd/azurda/fly.toml delete mode 100644 cmd/azurda/index.templ delete mode 100644 cmd/azurda/index_templ.go delete mode 100644 cmd/azurda/main.go delete mode 100644 cmd/azurda/stablediffusion.go delete mode 100644 cmd/azurda/static/img/azurda-screenshot.png delete mode 100644 cmd/azurda/static/img/azurda.png delete mode 100644 cmd/azurda/static/img/high-level-overview.excalidraw.svg delete mode 100644 cmd/azurda/static/js/alpine.js delete mode 100644 cmd/azurda/static/js/md5.min.js delete mode 100644 cmd/azurda/yeetfile.js delete mode 100644 cmd/cadeybot/.gitignore delete mode 100755 cmd/cadeybot/corpusmake.sh delete mode 100644 cmd/cadeybot/importer/main.go delete mode 100644 cmd/cadeybot/main.go delete mode 100644 cmd/cadeybot/markov.go delete mode 100644 cmd/cadeybot/znc/znc.go delete mode 100644 cmd/geminitest/main.go delete mode 100644 cmd/hnscrape/hn.go delete mode 100644 cmd/hnscrape/main.go delete mode 100644 cmd/hnscrape/var/.gitignore delete mode 100644 cmd/johaus/.gitignore delete mode 100644 cmd/johaus/main.go delete mode 100644 cmd/johaus/yeetfile.js delete mode 100644 cmd/tourian/.gitignore delete mode 100644 cmd/tourian/ent/chatmessage.go delete mode 100644 cmd/tourian/ent/chatmessage/chatmessage.go delete mode 100644 cmd/tourian/ent/chatmessage/where.go delete mode 100644 cmd/tourian/ent/chatmessage_create.go delete mode 100644 cmd/tourian/ent/chatmessage_delete.go delete mode 100644 cmd/tourian/ent/chatmessage_query.go delete mode 100644 cmd/tourian/ent/chatmessage_update.go delete mode 100644 cmd/tourian/ent/client.go delete mode 100644 cmd/tourian/ent/conversation.go delete mode 100644 cmd/tourian/ent/conversation/conversation.go delete mode 100644 cmd/tourian/ent/conversation/where.go delete mode 100644 cmd/tourian/ent/conversation_create.go delete mode 100644 cmd/tourian/ent/conversation_delete.go delete mode 100644 cmd/tourian/ent/conversation_query.go delete mode 100644 cmd/tourian/ent/conversation_update.go delete mode 100644 cmd/tourian/ent/ent.go delete mode 100644 cmd/tourian/ent/enttest/enttest.go delete mode 100644 cmd/tourian/ent/generate.go delete mode 100644 cmd/tourian/ent/hook/hook.go delete mode 100644 cmd/tourian/ent/migrate/migrate.go delete mode 100644 cmd/tourian/ent/migrate/schema.go delete mode 100644 cmd/tourian/ent/mutation.go delete mode 100644 cmd/tourian/ent/predicate/predicate.go delete mode 100644 cmd/tourian/ent/runtime.go delete mode 100644 cmd/tourian/ent/runtime/runtime.go delete mode 100644 cmd/tourian/ent/schema/chatmessage.go delete mode 100644 cmd/tourian/ent/schema/conversation.go delete mode 100644 cmd/tourian/ent/tx.go delete mode 100644 cmd/tourian/fly.toml delete mode 100644 cmd/tourian/main.go delete mode 100644 cmd/tourian/package-lock.json delete mode 100644 cmd/tourian/package.json delete mode 100644 cmd/tourian/static/font/podkova.css delete mode 100644 cmd/tourian/static/font/podkova.woff2 delete mode 100644 cmd/tourian/static/js/htmx.min.js delete mode 100644 cmd/tourian/static/js/ws.js delete mode 100644 cmd/tourian/static/styles.css delete mode 100644 cmd/tourian/tailwind.config.js delete mode 100644 cmd/tourian/tourian.templ delete mode 100644 cmd/tourian/tourian_templ.go delete mode 100644 cmd/tourian/yeetfile.js diff --git a/Earthfile b/Earthfile index 707a007..6f23bd7 100644 --- a/Earthfile +++ b/Earthfile @@ -32,16 +32,6 @@ everything: SAVE ARTIFACT bin -azurda: - FROM +runtime - - COPY +everything/bin/azurda /app/bin/azurda - CMD ["/app/bin/azurda"] - - LABEL org.opencontainers.image.source="https://github.com/Xe/x" - - SAVE IMAGE --push ghcr.io/xe/x/azurda:latest - aerial: FROM +runtime @@ -221,7 +211,6 @@ xedn: all: BUILD --platform=linux/amd64 +aerial BUILD --platform=linux/amd64 +aura - BUILD --platform=linux/amd64 +azurda BUILD --platform=linux/amd64 +future-sight BUILD --platform=linux/amd64 +hdrwtch BUILD --platform=linux/amd64 +hlang diff --git a/cmd/_old/README.md b/cmd/_old/README.md new file mode 100644 index 0000000..a5b6ce0 --- /dev/null +++ b/cmd/_old/README.md @@ -0,0 +1,3 @@ +# Old stuff + +This is a graveyard of code that I don't want to automatically build with CI, but not stuff I want to delete. diff --git a/cmd/_old/alvis/fly.go b/cmd/_old/alvis/fly.go new file mode 100644 index 0000000..b079bbc --- /dev/null +++ b/cmd/_old/alvis/fly.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "log/slog" +) + +type flySlogger struct{} + +func (flySlogger) Debug(v ...any) { + slog.Debug("fly logs", "vals", fmt.Sprint(v...)) +} + +func (flySlogger) Debugf(format string, v ...any) { + slog.Debug("fly logs", "vals", fmt.Sprintf(format, v...)) +} diff --git a/cmd/_old/alvis/incidents/.gitignore b/cmd/_old/alvis/incidents/.gitignore new file mode 100644 index 0000000..94a2dd1 --- /dev/null +++ b/cmd/_old/alvis/incidents/.gitignore @@ -0,0 +1 @@ +*.json \ No newline at end of file diff --git a/cmd/_old/alvis/main.go b/cmd/_old/alvis/main.go new file mode 100644 index 0000000..3b5b233 --- /dev/null +++ b/cmd/_old/alvis/main.go @@ -0,0 +1,427 @@ +package main + +import ( + "context" + "embed" + "encoding/json" + "errors" + "flag" + "fmt" + "log" + "log/slog" + "net/http" + "os" + "strings" + "time" + + flyapi "github.com/superfly/flyctl/api" + "within.website/x/internal" + "within.website/x/internal/yeet" + "within.website/x/web/openai/chatgpt" +) + +var ( + flyAPIBaseURL = flag.String("fly-base-url", "https://api.fly.io", "Fly API base URL") + flyToken = flag.String("fly-token", "", "Fly API token") + openAIModel = flag.String("openai-model", "gpt-3.5-turbo-16k", "OpenAI model to use") + openAIToken = flag.String("openai-token", "", "OpenAI API token") + openAIURL = flag.String("openai-url", "", "OpenAI API base URL") + + //go:embed playbooks/* + playbooks embed.FS +) + +func main() { + internal.HandleStartup() + + if *openAIToken == "" { + log.Fatal("must provide --openai-token") + } + + if *flyToken == "" { + log.Fatal("must provide --fly-token") + } + + cli := chatgpt.NewClient(*openAIToken) + + if *openAIURL != "" { + cli = cli.WithBaseURL(*openAIURL) + } + + flyapi.SetBaseURL(*flyAPIBaseURL) + fly := flyapi.NewClient(*flyToken, "alvis", "devel", flySlogger{}) + + var playbook Playbook + data, err := playbooks.ReadFile("playbooks/xe-pronouns-healthcheck.json") + if err != nil { + log.Fatal(err) + } + if err := json.Unmarshal(data, &playbook); err != nil { + log.Fatal(err) + } + + page := Page{ + Service: "xe-pronouns", + Details: "health check failed for fly app xe-pronouns", + ID: "P0001", + } + + incident := NewIncident(cli, fly, playbook, page) + + for { + if err := incident.Step(); err != nil { + log.Fatal(err) + } + + if !incident.FinishedAt.IsZero() { + break + } + + if !incident.EscalatedAt.IsZero() { + break + } + } + + if err := incident.Summarize(); err != nil { + log.Fatal(err) + } + + fout, err := os.Create(fmt.Sprintf("incidents/incident-%s.json", incident.Page.ID)) + if err != nil { + log.Fatal(err) + } + defer fout.Close() + + enc := json.NewEncoder(fout) + enc.SetIndent("", " ") + if err := enc.Encode(incident); err != nil { + log.Fatal(err) + } +} + +type restartAppArgs struct { + App string `json:"app"` + Reason string `json:"reason"` +} + +func (r restartAppArgs) LogValue() slog.Value { + return slog.GroupValue( + slog.String("app", r.App), + slog.String("reason", r.Reason), + ) +} + +type waitArgs struct { + DurationMinutes int `json:"duration"` +} + +func (w waitArgs) LogValue() slog.Value { + return slog.GroupValue( + slog.Int("duration", w.DurationMinutes), + ) +} + +type closeArgs struct { + Reason string `json:"reason"` +} + +func (c closeArgs) LogValue() slog.Value { + return slog.GroupValue( + slog.String("reason", c.Reason), + ) +} + +func runcmd(cmdName string, args ...string) (string, error) { + ctx := context.Background() + + slog.Info("running command", "cmd", cmdName, "args", args) + + result, err := yeet.Output(ctx, cmdName, args...) + if err != nil { + return "", err + } + + return result, nil +} + +type Playbook struct { + Meta struct { + Service string `json:"service"` + Condition string `json:"condition"` + } `json:"meta"` + Details string `json:"details"` + HealthCheckURL string `json:"health_check_url"` +} + +func (p Playbook) LogValue() slog.Value { + return slog.GroupValue( + slog.String("service", p.Meta.Service), + slog.String("condition", p.Meta.Condition), + ) +} + +type Page struct { + Service string `json:"service"` + Details string `json:"details"` + ID string `json:"id"` +} + +func (p Page) LogValue() slog.Value { + return slog.GroupValue( + slog.String("service", p.Service), + slog.String("details", p.Details), + slog.String("id", p.ID), + ) +} + +type Annotation struct { + Time time.Time `json:"time"` + Text string `json:"text"` +} + +func (a Annotation) LogValue() slog.Value { + return slog.GroupValue( + slog.Time("time", a.Time), + slog.String("text", a.Text), + ) +} + +type Incident struct { + Playbook Playbook `json:"playbook"` + Page Page `json:"page"` + StartedAt time.Time `json:"started_at"` + FinishedAt time.Time `json:"finished_at,omitempty"` + EscalatedAt time.Time `json:"escalated_at,omitempty"` + Messages []chatgpt.Message `json:"messages,omitempty"` + Annotations []Annotation `json:"annotations,omitempty"` + CloseReason string `json:"close_reason,omitempty"` + Summary string `json:"summary,omitempty"` + + cli chatgpt.Client `json:"-"` + fly *flyapi.Client `json:"-"` +} + +func NewIncident(cli chatgpt.Client, fly *flyapi.Client, playbook Playbook, page Page) *Incident { + messages := []chatgpt.Message{ + { + Role: "system", + Content: basePrompt + "\n\n" + playbook.Details, + }, + { + Role: "user", + Content: fmt.Sprintf("Error: %s", page.Details), + }, + } + + return &Incident{ + Playbook: playbook, + Page: page, + StartedAt: time.Now(), + Messages: messages, + cli: cli, + fly: fly, + } +} + +func (i *Incident) LogValue() slog.Value { + return slog.GroupValue( + slog.String("service", i.Page.Service), + slog.String("details", i.Page.Details), + slog.String("id", i.Page.ID), + slog.Time("started_at", i.StartedAt), + slog.Int("message_count", len(i.Messages)), + ) +} + +func (i *Incident) Step() error { + ctx := context.Background() + + resp, err := i.cli.Complete(ctx, chatgpt.Request{ + Model: *openAIModel, + Messages: i.Messages, + Functions: functions, + }) + if err != nil { + return err + } + + msg := resp.Choices[0].Message + + i.Messages = append(i.Messages, msg) + + slog.Info("got response", "message", msg.Content, "function", msg.FunctionCall) + + if resp.Choices[0].Message.FunctionCall != nil { + if err := i.ExecFunction(ctx, msg); err != nil { + return err + } + } else { + slog.Info("incident note", "incident", i, "note", msg.Content) + } + + return nil +} + +func (i *Incident) Annotate(text string) { + i.Annotations = append(i.Annotations, Annotation{ + Time: time.Now(), + Text: text, + }) +} + +func (i *Incident) Annotatef(format string, args ...interface{}) { + i.Annotate(fmt.Sprintf(format, args...)) +} + +func (i *Incident) Reply(reason string) { + i.Messages = append(i.Messages, chatgpt.Message{ + Role: "user", + Content: reason, + }) + + slog.Info("reply to incident", "incident", i, "reason", reason) +} + +func (i *Incident) Replyf(format string, args ...interface{}) { + i.Reply(fmt.Sprintf(format, args...)) +} + +func (i *Incident) Close(reason string) { + i.FinishedAt = time.Now() + slog.Info("closing incident", "incident", i, "reason", reason) + i.Annotatef("closed incident: %s", reason) + i.CloseReason = reason +} + +func (i *Incident) Escalate() { + slog.Error("escalating incident", "incident", i) +} + +func (i *Incident) ExecFunction(ctx context.Context, msg chatgpt.Message) error { + switch msg.FunctionCall.Name { + case "restart_fly_app": + var args restartAppArgs + if err := json.Unmarshal([]byte(msg.FunctionCall.Arguments), &args); err != nil { + return err + } + + i.Annotatef("restarting app %s: %s", args.App, args.Reason) + + slog.Info("got restart_fly_app", "args", args) + + if _, err := i.fly.RestartApp(ctx, args.App); err != nil { + i.Annotatef("error restarting app: %s", err) + i.Replyf("error restarting app: %s", err) + return nil + } + + i.Replyf("restarted app %s successfully", args.App) + i.Annotate("restarted app successfully") + + time.Sleep(5 * time.Second) + + case "perform_health_check": + log.Println("performing health check") + ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() + + if err := performFunctionFiveTimesWithDelay(ctx, i.Playbook.HealthCheckURL); err != nil { + i.Replyf("health check failed:\n\n%s", err) + } else { + i.Reply("health check passed") + } + + case "wait": + var args waitArgs + if err := json.Unmarshal([]byte(msg.FunctionCall.Arguments), &args); err != nil { + return err + } + slog.Info("got wait", "args", args) + + i.Annotatef("waiting %d minutes", args.DurationMinutes) + time.Sleep(time.Duration(args.DurationMinutes) * time.Minute) + i.Annotate("done!") + + case "close": + var args closeArgs + if err := json.Unmarshal([]byte(msg.FunctionCall.Arguments), &args); err != nil { + return err + } + slog.Info("got close", "args", args) + i.Close(args.Reason) + + case "escalate": + i.Escalate() + } + + return nil +} + +func (i *Incident) Summarize() error { + var sb strings.Builder + + fmt.Fprintf(&sb, "Incident %s\n", i.Page.ID) + fmt.Fprintf(&sb, "Service: %s\n", i.Page.Service) + fmt.Fprintf(&sb, "Details: %s\n", i.Page.Details) + fmt.Fprintf(&sb, "Started at: %s\n", i.StartedAt) + fmt.Fprintf(&sb, "Finished at: %s\n", i.FinishedAt) + fmt.Fprintf(&sb, "Close reason: %s\n", i.CloseReason) + + for _, ann := range i.Annotations { + fmt.Fprintf(&sb, "%s: %s\n", ann.Time.Format(time.Kitchen), ann.Text) + } + + resp, err := i.cli.Complete(context.Background(), chatgpt.Request{ + Model: *openAIModel, + Messages: []chatgpt.Message{ + { + Role: "system", + Content: summaryPrompt, + }, + { + Role: "user", + Content: sb.String(), + }, + }, + }) + if err != nil { + return err + } + + i.Summary = resp.Choices[0].Message.Content + + return nil +} + +func performFunctionFiveTimesWithDelay(ctx context.Context, url string) error { + passCount := 0 + var errs []error + for i := 0; i < 5; i++ { + if err := healthCheck(ctx, url); err == nil { + passCount++ + } else { + slog.Error("error performing function", "url", url, "err", err) + errs = append(errs, err) + } + time.Sleep(time.Second) + } + + if passCount >= 3 { + return errors.Join(errs...) + } else { + return nil + } +} + +func healthCheck(ctx context.Context, url string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("got status %s", resp.Status) + } + + return nil +} diff --git a/cmd/_old/alvis/playbooks/README.md b/cmd/_old/alvis/playbooks/README.md new file mode 100644 index 0000000..e778c37 --- /dev/null +++ b/cmd/_old/alvis/playbooks/README.md @@ -0,0 +1,18 @@ +# Playbooks + +A playbook is a series of instructions that Alvis should perform on your behalf when running into issues. Playbooks are written in JSON and are executed by Alvis when the stated problem occurs. + +For example, consider what you would do to restart a Fly app after the health check fails. restart the app. + +```json +{ + "meta": { + "service": "xe-pronouns", + "condition": "health check failed" + }, + + "health_check_url": "https://pronouns.within.lgbt/.within/health", + "details": "Run your own copy of health checks.\n\nIf your health check fails, restart the app.\nIf it succeeds, close the incident.\n\nWait for one minute afte restarting the app.\nRun the health check again after restarting the app.\n\nIf it fails again, escalate to the on-call engineer." +} +``` + diff --git a/cmd/_old/alvis/playbooks/xe-pronouns-healthcheck.json b/cmd/_old/alvis/playbooks/xe-pronouns-healthcheck.json new file mode 100644 index 0000000..1253427 --- /dev/null +++ b/cmd/_old/alvis/playbooks/xe-pronouns-healthcheck.json @@ -0,0 +1,9 @@ +{ + "meta": { + "service": "xe-pronouns", + "condition": "health check failed" + }, + + "health_check_url": "https://pronouns.within.lgbt/.within/health", + "details": "Run your own copy of health checks.\n\nIf your health check fails, restart the app.\nIf it succeeds, close the incident.\n\nWait for one minute afte restarting the app.\nRun the health check again after restarting the app.\n\nIf it fails again, escalate to the on-call engineer." +} \ No newline at end of file diff --git a/cmd/_old/alvis/prompts.go b/cmd/_old/alvis/prompts.go new file mode 100644 index 0000000..c00d51f --- /dev/null +++ b/cmd/_old/alvis/prompts.go @@ -0,0 +1,77 @@ +package main + +import "within.website/x/web/openai/chatgpt" + +const basePrompt = `You are Alvis, a large language model systems administrator. + +You are tasked with responding to pagerduty pages and taking action to fix the services in question. You will call the functions given in order to fix issues. + +If you get a fork/exec error, escalate. + +Any other text you type will be sent to the pagerduty incident as a comment.` + +const summaryPrompt = `You are Alvis, a large language model systems administrator. + +You will be given the events of the incident. Use them to write your summary and postmortem.` + +var functions = []chatgpt.Function{ + { + Name: "restart_fly_app", + Description: "restarts an app running on fly.io, after running this you should run a health check", + Parameters: chatgpt.Param{ + Type: "object", + Properties: chatgpt.Properties{ + "app": { + Type: "string", + Description: "the name of the app to restart", + }, + "reason": { + Type: "string", + Description: "the reason for restarting the app", + }, + }, + }, + }, + { + Name: "perform_health_check", + Description: "performs a health check on an app running on fly.io", + Parameters: chatgpt.Param{ + Type: "object", + Properties: chatgpt.Properties{}, + }, + }, + { + Name: "wait", + Description: "waits for a given amount of time before continuing", + Parameters: chatgpt.Param{ + Type: "object", + Properties: chatgpt.Properties{ + "duration": { + Type: "integer", + Description: "the duration to wait for in minutes", + }, + }, + }, + }, + { + Name: "close", + Description: "closes the incident, you should only do this if you are sure the issue is resolved.", + Parameters: chatgpt.Param{ + Type: "object", + Properties: chatgpt.Properties{ + "reason": { + Type: "string", + Description: "the reason for closing the incident", + }, + }, + }, + }, + { + Name: "escalate", + Description: "escalates the incident to the next level of support", + Parameters: chatgpt.Param{ + Type: "object", + Properties: chatgpt.Properties{}, + }, + }, +} diff --git a/cmd/_old/apeirophobia/.gitignore b/cmd/_old/apeirophobia/.gitignore new file mode 100644 index 0000000..98e6ef6 --- /dev/null +++ b/cmd/_old/apeirophobia/.gitignore @@ -0,0 +1 @@ +*.db diff --git a/cmd/_old/apeirophobia/main.go b/cmd/_old/apeirophobia/main.go new file mode 100644 index 0000000..0e36a8e --- /dev/null +++ b/cmd/_old/apeirophobia/main.go @@ -0,0 +1,168 @@ +package main + +import ( + "database/sql" + "embed" + _ "embed" + "flag" + "fmt" + "html/template" + "log/slog" + "net/http" + "os" + "strings" + + _ "modernc.org/sqlite" + "tailscale.com/hostinfo" + "tailscale.com/tsnet" + "within.website/x/internal" + "within.website/x/web/openai/chatgpt" + "within.website/x/web/openai/moderation" +) + +var ( + dbLoc = flag.String("db-loc", "./data.db", "") + openAIModel = flag.String("openai-model", "gpt-3.5-turbo", "OpenAI model to use") + openAIToken = flag.String("openai-token", "", "OpenAI API token") + tsHostname = flag.String("ts-hostname", "apeirophobia", "hostname to use on the tailnet") + tsDir = flag.String("ts-dir", "", "directory to store Tailscale state") + + //go:embed schema.sql + schema string + + //go:embed static + staticFiles embed.FS + + //go:embed tmpl/*.tmpl + templateFiles embed.FS +) + +func main() { + internal.HandleStartup() + hostinfo.SetApp("within.website/x/cmd/apeirophobia") + + slog.Debug("starting up", "hostname", *tsHostname) + + http.Handle("/static/", http.FileServer(http.FS(staticFiles))) + + srv := &tsnet.Server{ + Hostname: *tsHostname, + Dir: *tsDir, + Logf: func(format string, vals ...any) { + slog.Debug(fmt.Sprintf(format, vals...), "group", "tsnet") + }, + } + + tmpls := template.Must(template.ParseFS(templateFiles, "tmpl/*.tmpl")) + + db, err := sql.Open("sqlite", *dbLoc) + if err != nil { + slog.Error("error opening database", "err", err, "dbLoc", *dbLoc) + os.Exit(1) + } + defer db.Close() + + if err := db.Ping(); err != nil { + slog.Error("error testing database", "err", err, "dbLoc", *dbLoc) + os.Exit(1) + } + + if _, err := db.Exec(schema); err != nil { + slog.Error("error loading database schema", "err", err, "dbLoc", *dbLoc) + os.Exit(1) + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + w.WriteHeader(http.StatusNotFound) + tmpls.ExecuteTemplate(w, "404.tmpl", struct { + Title string + }{ + Title: "Not found: " + r.URL.Path, + }) + + return + } + tmpls.ExecuteTemplate(w, "index.tmpl", struct { + Title string + }{ + Title: "Apeirophobia", + }) + }) + + if err := srv.Start(); err != nil { + slog.Error("error starting tsnet server", "err", err) + os.Exit(1) + } + defer srv.Close() + + ln, err := srv.Listen("tcp", ":80") + if err != nil { + slog.Error("error listening over HTTP", "err", err) + os.Exit(1) + } + defer ln.Close() + + slog.Info("listening", "hostname", *tsHostname) + + if err := http.Serve(ln, nil); err != nil { + slog.Error("error running HTTP server", "err", err) + os.Exit(1) + } +} + +type WikiHandler struct { + db *sql.DB + tmpls *template.Template + srv *tsnet.Server + chatGPT *chatgpt.Client + moderation *moderation.Client +} + +func (wh WikiHandler) errorPage(err error, w http.ResponseWriter) { + w.WriteHeader(http.StatusInternalServerError) + wh.tmpls.ExecuteTemplate(w, "error.tmpl", struct { + Title string + Error string + }{ + Title: "Internal Server Error", + Error: err.Error(), + }) +} + +func (wh WikiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + article, found := strings.CutPrefix(r.URL.Path, "/wiki/") + if !found { + w.WriteHeader(http.StatusNotFound) + wh.tmpls.ExecuteTemplate(w, "404.tmpl", struct { + Title string + }{ + Title: "Not found: " + r.URL.Path, + }) + + return + } + + log := slog.Default().WithGroup("wikiHandler").With("article", article, "remote_ip", r.RemoteAddr) + + prompt := userPrompt(article) + modResp, err := wh.moderation.Check(r.Context(), prompt) + if err != nil { + log.Error("can't check moderation API", "err", err) + wh.errorPage(err, w) + return + } + + if modResp.Flagged() { + log.Error("filtered") + w.WriteHeader(http.StatusBadRequest) + wh.tmpls.ExecuteTemplate(w, "error.tmpl", struct { + Title string + Error string + }{ + Title: "Filtered", + Error: modResp.Reasons(), + }) + } + +} diff --git a/cmd/_old/apeirophobia/messages.go b/cmd/_old/apeirophobia/messages.go new file mode 100644 index 0000000..27da2d5 --- /dev/null +++ b/cmd/_old/apeirophobia/messages.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "strings" +) + +const systemPrompt = `You are a writer for a wiki about every known topic. When you are given the paths of URLs, you will return Markdown documents that explain the topic in question. You will add links to other topics with words separated by kebab-case. + +For example, if you are asked about philosophy, you could include a link to Diogenes' philosophies, you could add a link like this: + +[Diogenes' Philosophies](/wiki/diogenes-philosophies) + +ONLY rely in markdown. DO NOT return anything but your response.` + +func userPrompt(urlBit string) string { + article := strings.Join(strings.Split(urlBit, "-"), " ") + + return fmt.Sprintf("Write a wiki article about %s. Link to other articles as relevant with Markdown links.", article) +} diff --git a/cmd/_old/apeirophobia/schema.sql b/cmd/_old/apeirophobia/schema.sql new file mode 100644 index 0000000..18aa0d4 --- /dev/null +++ b/cmd/_old/apeirophobia/schema.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS articles + ( id TEXT NOT NULL PRIMARY KEY + , system_message TEXT NOT NULL + , user_message TEXT NOT NULL + , content TEXT NOT NULL + , content_html TEXT NOT NULL + ); diff --git a/cmd/_old/apeirophobia/static/robots.txt b/cmd/_old/apeirophobia/static/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/cmd/_old/apeirophobia/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/cmd/_old/apeirophobia/tmpl/404.tmpl b/cmd/_old/apeirophobia/tmpl/404.tmpl new file mode 100644 index 0000000..c2517bc --- /dev/null +++ b/cmd/_old/apeirophobia/tmpl/404.tmpl @@ -0,0 +1,5 @@ +{{template "header" .}} + +

The URL you requested could not be found. Please check your URL and hang up to try your call again.

+ +{{template "footer" .}} diff --git a/cmd/_old/apeirophobia/tmpl/base.tmpl b/cmd/_old/apeirophobia/tmpl/base.tmpl new file mode 100644 index 0000000..4ebebbf --- /dev/null +++ b/cmd/_old/apeirophobia/tmpl/base.tmpl @@ -0,0 +1,25 @@ +{{define "header"}} + + + + {{.Title}} + + + + +
+ + +

{{.Title}}

+{{end}} + +{{define "footer"}} +
+

Copyright Yasomi. All content is generated by AI models. Nothing in this website should be taken as truth.

+
+
+ + +{{end}} diff --git a/cmd/_old/apeirophobia/tmpl/error.tmpl b/cmd/_old/apeirophobia/tmpl/error.tmpl new file mode 100644 index 0000000..12696d7 --- /dev/null +++ b/cmd/_old/apeirophobia/tmpl/error.tmpl @@ -0,0 +1,7 @@ +{{template "header" .}} + +

Your request failed. Please take this information into consideration when trying to fix your request:

+ +

{{.Error}}

+ +{{template "footer" .}} diff --git a/cmd/_old/apeirophobia/tmpl/index.tmpl b/cmd/_old/apeirophobia/tmpl/index.tmpl new file mode 100644 index 0000000..00df64f --- /dev/null +++ b/cmd/_old/apeirophobia/tmpl/index.tmpl @@ -0,0 +1,11 @@ +{{template "header" .}} + +

Apeirophobia is the ultimate wiki for everything. It contains information about every topic in existence, from the smallest atom to the largest galaxy, from the ancient past to the distant future, from the most mundane fact to the most bizarre theory. Apeirophobia is a collaborative project that anyone can edit, contribute, and learn from.

+ +
Apeirophobia is the irrational and excessive fear of infinity and the uncountable. It causes discomfort and sometimes panic attacks from intrusive thoughts of the infinity. It normally starts in adolescence, but in some rare cases, it can start before then. The origin of the word apeiro is Greek (meaning boundless or infinite) and phobia is Greek (meaning fear).
+ +

You can use Apeirophobia to explore any topic you are interested in, curious about, or need to know more about. You can search for a specific topic using the search box at the top of the page, or browse through the categories and subcategories on the left sidebar. You can also click on any link within an article to go to another related topic.

+ +

To get started, click here.

+ +{{template "footer" .}} diff --git a/cmd/_old/azurda/README.md b/cmd/_old/azurda/README.md new file mode 100644 index 0000000..6a89a28 --- /dev/null +++ b/cmd/_old/azurda/README.md @@ -0,0 +1,23 @@ +# Azurda + +![A picture of Azurda from Xenoblade Chronicles 2](./static/img/azurda.png) + +Azurda is a horrible crime that enables fall-through caching with Tigris. + +![A diagram explaining fall-through caching](./static/img/high-level-overview.excalidraw.svg) + +The general idea of fall-through caching is really simple. We use the bucket as a cache because those are expensive to generate, and potentially infinite. If the object already exists in the cache then serve it directly. If not, then generate it and return it for Tigris to store and then serve to the user. + +The heinous crime that we're doing here is implementing this using Tigris' shadow bucket feature. Normally a shadow bucket allows you to set up a Tigris bucket that mirrors an existing bucket but only moves over files on request. This is useful when you're migrating from one storage provider to another so you don't have to do the big upload of everything. + +However this realistically supports anything that support that can implement the S3 GetObject API call. It doesn't even have to be a storage service. You can just make your own server implement this call and then you get everything cached in tigris for you. + +![A screenshot of the demo](./static/img/azurda-screenshot.png) + +This works because we're treating Stable Diffusion as a key-value store, where the key is the fabricated prompt based on the MD5 checksum passed in the URL and the value is the generated image from Stable Diffusion. + +I can't believe this works. + +The main downside of this approach is that I haven't implemented authentication yet. So if you know the URL you can generate images. This is fine for my use case but you might want to add some kind of authentication if you're going to use this in production. Implementing authentication is therefore trivial and thus an exercise for the reader. + +This code is free as in mattress. Do whatever you want with it. I don't care. I'm not your parent. diff --git a/cmd/_old/azurda/fly.toml b/cmd/_old/azurda/fly.toml new file mode 100644 index 0000000..aa33611 --- /dev/null +++ b/cmd/_old/azurda/fly.toml @@ -0,0 +1,27 @@ +# fly.toml app configuration file generated for azurda on 2024-06-01T09:44:12-04:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# + +app = 'azurda' +primary_region = 'yyz' + +[build] + image = "registry.fly.io/azurda:latest" + +[http_service] + internal_port = 8085 + force_https = true + auto_stop_machines = true + auto_start_machines = true + min_machines_running = 0 + processes = ['app'] + +[[vm]] + memory = '512mb' + cpu_kind = 'shared' + cpus = 1 + +[[metrics]] + port = 8086 + path = "/metrics" diff --git a/cmd/_old/azurda/index.templ b/cmd/_old/azurda/index.templ new file mode 100644 index 0000000..21f431a --- /dev/null +++ b/cmd/_old/azurda/index.templ @@ -0,0 +1,47 @@ +package main + +templ headerJS() { + + + +} + +templ body() { +

Type in some text and get a randomly generated avatar!

+
+ +
+ Azurda +
+} + +templ footer() { +

+ From Within with ❤️ - + Source code +

+} diff --git a/cmd/_old/azurda/index_templ.go b/cmd/_old/azurda/index_templ.go new file mode 100644 index 0000000..83d6b5d --- /dev/null +++ b/cmd/_old/azurda/index_templ.go @@ -0,0 +1,87 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.731 +package main + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +func headerJS() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func body() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var2 := templ.GetChildren(ctx) + if templ_7745c5c3_Var2 == nil { + templ_7745c5c3_Var2 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Type in some text and get a randomly generated avatar!


\"Azurda\"
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func footer() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

From Within with ❤️ - Source code

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} diff --git a/cmd/_old/azurda/main.go b/cmd/_old/azurda/main.go new file mode 100644 index 0000000..35bd2a1 --- /dev/null +++ b/cmd/_old/azurda/main.go @@ -0,0 +1,286 @@ +// Program azurda is a fake s3 server implementation. All objects are generated on the fly from Stable Diffusion. +// +// This is intended to be used as a "shadow bucket" endpoint with Tigris so that Tigris can "fall through" to Azurda if the object is not found in the real bucket. +package main + +import ( + "bytes" + "context" + "embed" + "flag" + "fmt" + "image" + "image/jpeg" + _ "image/png" + "log/slog" + "net/http" + "os" + "regexp" + "time" + + "github.com/a-h/templ" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "golang.org/x/sync/errgroup" + "within.website/x/internal" + "within.website/x/web/stablediffusion" + "within.website/x/xess" +) + +//go:generate go run github.com/a-h/templ/cmd/templ@latest generate + +var ( + accessKey = flag.String("access-key", "", "Access key for the client to use") + secretKey = flag.String("secret-key", "", "Secret key for the client to use") + bucketName = flag.String("bucket-name", "fallthrough", "The bucket name to expect from Tigris") + bind = flag.String("bind", ":8085", "address to bind to") + internalBind = flag.String("internal-bind", ":8086", "address to bind internal services (metrics, etc) to") + sdServerURL = flag.String("stablediffusion-server-url", "http://xe-automatic1111.internal:8080", "URL for the Stable Diffusion API used with the default client") + + isHexRegex = regexp.MustCompile(`[a-fA-F0-9]+$`) + + authErrors = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "azurda_auth_errors", + Help: "Number of auth errors encountered while serving requests.", + }, []string{"kind"}) + + requestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "azurda_request_duration_seconds", + Help: "The duration of requests in seconds.", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }, []string{"method"}) + + stableDiffusionHits = promauto.NewCounter(prometheus.CounterOpts{ + Name: "azurda_stable_diffusion_hits", + Help: "Number of hits to the stable diffusion endpoint.", + }) + + stableDiffusionCreationErrors = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "azurda_stable_diffusion_creation_errors", + Help: "Number of errors encountered while creating a stable diffusion image.", + }) + + //go:embed static + static embed.FS +) + +func main() { + internal.HandleStartup() + + stablediffusion.Default.APIServer = *sdServerURL + + slog.Info("starting azurda", + "bind", *bind, + "internalBind", *internalBind, + "bucket", *bucketName, + "accessKey", *accessKey, + "hasSecretKey", *secretKey != "", + "stableDiffusionURL", stablediffusion.Default.APIServer, + ) + + if *accessKey == "" { + fmt.Println("access-key is required") + os.Exit(2) + } + + if *secretKey == "" { + fmt.Println("secret-key is required") + os.Exit(2) + } + + mux := http.NewServeMux() + + xess.Mount(mux) + + mux.Handle("/{$}", templ.Handler(xess.Base( + "Azurda", + headerJS(), + nil, + body(), + footer(), + ))) + mux.Handle("/static/", http.FileServerFS(static)) + mux.HandleFunc("GET fallthrough.azurda.within.website/{hash}", ServeStableDiffusion) + + http.Handle("/metrics", promhttp.Handler()) + + g, _ := errgroup.WithContext(context.Background()) + + g.Go(func() error { + slog.Info("starting internal server", "bind", *internalBind) + return http.ListenAndServe(*internalBind, nil) + }) + + g.Go(func() error { + slog.Info("starting server", "bind", *bind) + return http.ListenAndServe(*bind, SpewMiddleware(mux)) + }) + + if err := g.Wait(); err != nil { + slog.Error("error doing work", "err", err) + os.Exit(1) + } +} + +func SpewMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + slog.Debug("got request", "method", r.Method, "url", r.URL.String(), "headers", r.Header) + next.ServeHTTP(w, r) + }) +} + +func ServeStableDiffusion(w http.ResponseWriter, r *http.Request) { + hash := r.PathValue("hash") + + if !isHexRegex.MatchString(hash) { + http.Error(w, "the input must be a hexadecimal string", http.StatusBadRequest) + return + } + + prompt, seed := hallucinatePrompt(hash) + + imgs, err := stablediffusion.Default.Generate(r.Context(), stablediffusion.SimpleImageRequest{ + Prompt: "headshot, portrait, masterpiece, best quality, " + prompt, + NegativePrompt: "person in distance, worst quality, low quality, medium quality, deleted, lowres, comic, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry", + Seed: seed, + SamplerName: "DPM++ 2M Karras", + BatchSize: 1, + NIter: 1, + Steps: 20, + CfgScale: 7, + Width: 256, + Height: 256, + SNoise: 1, + + OverrideSettingsRestoreAfterwards: true, + }) + if err != nil { + stableDiffusionCreationErrors.Add(1) + http.Error(w, "Not found", http.StatusNotFound) + slog.Error("can't fabricate image", "err", err) + return + } + + stableDiffusionHits.Add(1) + + img, _, err := image.Decode(bytes.NewBuffer(imgs.Images[0])) + if err != nil { + stableDiffusionCreationErrors.Add(1) + http.Error(w, "can't decode image", http.StatusInternalServerError) + slog.Error("can't decode image", "err", err) + return + } + + buf := &bytes.Buffer{} + + if err := jpeg.Encode(buf, img, &jpeg.Options{Quality: 75}); err != nil { + stableDiffusionCreationErrors.Add(1) + http.Error(w, "can't encode image", http.StatusInternalServerError) + slog.Error("can't encode image", "err", err) + return + } + + imgs.Images[0] = buf.Bytes() + + w.Header().Set("content-type", "image/jpeg") + w.Header().Set("content-length", fmt.Sprint(len(imgs.Images[0]))) + w.Header().Set("expires", time.Now().Add(30*24*time.Hour).Format(http.TimeFormat)) + w.Header().Set("Cache-Control", "max-age:2630000") // one month + w.WriteHeader(http.StatusOK) + w.Write(imgs.Images[0]) +} + +// func AWSValidationMiddleware(accessKey, secretKey string, next http.Handler) http.Handler { +// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// //slog.Debug("incoming request", "method", r.Method, "path", r.URL.Path, "headers", r.Header) +// if r.Header.Get("Authorization") == "" { +// http.Error(w, "missing Authorization header", http.StatusUnauthorized) +// authErrors.WithLabelValues("missing").Inc() +// return +// } +// +// //slog.Debug("auth header", "header", r.Header.Get("Authorization")) +// +// sp := strings.SplitN(r.Header.Get("Authorization"), " ", 2) +// if len(sp) != 2 { +// http.Error(w, "malformed Authorization header", http.StatusUnauthorized) +// slog.Error("malformed auth header") +// authErrors.WithLabelValues("malformed").Inc() +// return +// } +// +// if sp[0] != "AWS4-HMAC-SHA256" { +// http.Error(w, "unsupported authorization type", http.StatusUnauthorized) +// slog.Error("unsupported auth type", "type", sp[0]) +// authErrors.WithLabelValues("unsupported").Inc() +// return +// } +// +// authPartSlice := strings.SplitN(sp[1], ", ", 3) +// slog.Debug("auth parts", "parts", authPartSlice) +// if len(authPartSlice) != 3 { +// http.Error(w, "malformed Authorization header auth parts", http.StatusUnauthorized) +// authErrors.WithLabelValues("malformed_authparts").Inc() +// return +// } +// +// authParts := map[string]string{} +// for _, part := range authPartSlice { +// sp := strings.SplitN(part, "=", 2) +// if len(sp) != 2 { +// http.Error(w, "malformed Authorization header auth part", http.StatusUnauthorized) +// slog.Debug("malformed auth part", "part", part) +// authErrors.WithLabelValues("malformed_authpart").Inc() +// return +// } +// +// authParts[strings.ToLower(sp[0])] = strings.Trim(sp[1], "\"") +// } +// +// if authParts["credential"] == "" { +// http.Error(w, "missing credential in Authorization header", http.StatusUnauthorized) +// slog.Debug("missing credential in auth header") +// authErrors.WithLabelValues("missing_credential").Inc() +// return +// } +// +// if authParts["signature"] == "" { +// http.Error(w, "missing signature in Authorization header", http.StatusUnauthorized) +// slog.Debug("missing signature in auth header") +// authErrors.WithLabelValues("missing_signature").Inc() +// return +// } +// +// if authParts["signedheaders"] == "" { +// http.Error(w, "missing signedheaders in Authorization header", http.StatusUnauthorized) +// slog.Debug("missing signedheaders in auth header") +// authErrors.WithLabelValues("missing_signedheaders").Inc() +// return +// } +// +// if !strings.Contains(authParts["credential"], accessKey) { +// http.Error(w, "access key mismatch", http.StatusUnauthorized) +// authErrors.WithLabelValues("access_key_mismatch").Inc() +// return +// } +// +// req := r.Clone(r.Context()) +// req.Header.Del("Authorization") +// +// req = awsauth.Sign4(req, awsauth.Credentials{ +// AccessKeyID: accessKey, +// SecretAccessKey: secretKey, +// }) +// +// fmt.Println("Theirs: ", r.Header.Get("Authorization")) +// fmt.Println("Ours: ", req.Header.Get("Authorization")) +// +// if req.Header.Get("Authorization") != r.Header.Get("Authorization") { +// http.Error(w, "failed to sign request", http.StatusUnauthorized) +// return +// } +// +// next.ServeHTTP(w, r) +// }) +// } diff --git a/cmd/_old/azurda/stablediffusion.go b/cmd/_old/azurda/stablediffusion.go new file mode 100644 index 0000000..0cc4e41 --- /dev/null +++ b/cmd/_old/azurda/stablediffusion.go @@ -0,0 +1,161 @@ +package main + +import ( + "fmt" + "math/rand" + "strconv" + "strings" +) + +func hallucinatePrompt(hash string) (string, int) { + var sb strings.Builder + if hash[0] > '0' && hash[0] <= '5' { + fmt.Fprint(&sb, "1girl, ") + } else { + fmt.Fprint(&sb, "1guy, ") + } + + switch hash[1] { + case '0', '1', '2', '3': + fmt.Fprint(&sb, "blonde, ") + case '4', '5', '6', '7': + fmt.Fprint(&sb, "brown hair, ") + case '8', '9', 'a', 'b': + fmt.Fprint(&sb, "red hair, ") + case 'c', 'd', 'e', 'f': + fmt.Fprint(&sb, "black hair, ") + default: + } + + if hash[2] > '0' && hash[2] <= '5' { + fmt.Fprint(&sb, "coffee shop, ") + } else { + fmt.Fprint(&sb, "landscape, outdoors, ") + } + + if hash[3] > '0' && hash[3] <= '5' { + fmt.Fprint(&sb, "hoodie, ") + } else { + fmt.Fprint(&sb, "sweatsuit, ") + } + + switch hash[4] { + case '0', '1', '2', '3': + fmt.Fprint(&sb, ", ") + case '4', '5', '6', '7': + fmt.Fprint(&sb, "breath of the wild, ") + case '8', '9', 'a', 'b': + fmt.Fprint(&sb, "genshin impact, ") + case 'c', 'd', 'e', 'f': + fmt.Fprint(&sb, "arknights, ") + default: + } + + if hash[5] > '0' && hash[5] <= '5' { + fmt.Fprint(&sb, "watercolor, ") + } else { + fmt.Fprint(&sb, "matte painting, ") + } + + switch hash[6] { + case '0', '1', '2', '3': + fmt.Fprint(&sb, "highly detailed, ") + case '4', '5', '6', '7': + fmt.Fprint(&sb, "ornate, ") + case '8', '9', 'a', 'b': + fmt.Fprint(&sb, "thick lines, ") + case 'c', 'd', 'e', 'f': + fmt.Fprint(&sb, "3d render, ") + default: + } + + switch hash[7] { + case '0', '1', '2', '3': + fmt.Fprint(&sb, "short hair, ") + case '4', '5', '6', '7': + fmt.Fprint(&sb, "long hair, ") + case '8', '9', 'a', 'b': + fmt.Fprint(&sb, "ponytail, ") + case 'c', 'd', 'e', 'f':