commit 5a7e718666d0d909149b5902ec093e0c7075b43e Author: mirza Date: Fri Mar 7 11:12:53 2025 +0700 Initial commit diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..dbc481d --- /dev/null +++ b/.air.toml @@ -0,0 +1,46 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "./main" + cmd = "make build" + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go", ".*_templ.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html", "templ"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b368bec --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with "go test -c" +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +tmp/ + +# IDE specific files +.vscode +.idea + +# .env file +.env + +# Project build +main +*templ.go + +# OS X generated file +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..551fa53 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM golang:1.23-alpine AS build +RUN apk add --no-cache curl + +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN go install github.com/a-h/templ/cmd/templ@latest && \ + templ generate && \ + curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss && \ + chmod +x tailwindcss && \ + ./tailwindcss -i cmd/web/assets/css/input.css -o cmd/web/assets/css/output.css + +RUN go build -o main cmd/api/main.go + +FROM alpine:3.20.1 AS prod +WORKDIR /app +COPY --from=build /app/main /app/main +EXPOSE ${PORT} +CMD ["./main"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9f2edb9 --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +# Simple Makefile for a Go project + +# Build the application +all: build test +templ-install: + @if ! command -v templ > /dev/null; then \ + read -p "Go's 'templ' is not installed on your machine. Do you want to install it? [Y/n] " choice; \ + if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \ + go install github.com/a-h/templ/cmd/templ@latest; \ + if [ ! -x "$$(command -v templ)" ]; then \ + echo "templ installation failed. Exiting..."; \ + exit 1; \ + fi; \ + else \ + echo "You chose not to install templ. Exiting..."; \ + exit 1; \ + fi; \ + fi +tailwind: + @if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss; fi + + @chmod +x tailwindcss + +build: tailwind templ-install + @echo "Building..." + @templ generate + @./tailwindcss -i cmd/web/assets/css/input.css -o cmd/web/assets/css/output.css + @go build -o main cmd/api/main.go + +# Run the application +run: + @go run cmd/api/main.go +# Create DB container +docker-run: + @if docker compose up --build 2>/dev/null; then \ + : ; \ + else \ + echo "Falling back to Docker Compose V1"; \ + docker-compose up --build; \ + fi + +# Shutdown DB container +docker-down: + @if docker compose down 2>/dev/null; then \ + : ; \ + else \ + echo "Falling back to Docker Compose V1"; \ + docker-compose down; \ + fi + +# Test the application +test: + @echo "Testing..." + @go test ./... -v +# Integrations Tests for the application +itest: + @echo "Running integration tests..." + @go test ./internal/database -v + +# Clean the binary +clean: + @echo "Cleaning..." + @rm -f main + +# Live Reload +watch: + @if command -v air > /dev/null; then \ + air; \ + echo "Watching...";\ + else \ + read -p "Go's 'air' is not installed on your machine. Do you want to install it? [Y/n] " choice; \ + if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \ + go install github.com/air-verse/air@latest; \ + air; \ + echo "Watching...";\ + else \ + echo "You chose not to install air. Exiting..."; \ + exit 1; \ + fi; \ + fi + +.PHONY: all build run test clean watch tailwind docker-run docker-down itest templ-install diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f57d6f --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Project blueprint_fix + +One Paragraph of project description goes here + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. + +## MakeFile + +Run build make command with tests +```bash +make all +``` + +Build the application +```bash +make build +``` + +Run the application +```bash +make run +``` +Create DB container +```bash +make docker-run +``` + +Shutdown DB Container +```bash +make docker-down +``` + +DB Integrations Test: +```bash +make itest +``` + +Live reload the application: +```bash +make watch +``` + +Run the test suite: +```bash +make test +``` + +Clean up binary from the last build: +```bash +make clean +``` diff --git a/cmd/api/main.go b/cmd/api/main.go new file mode 100644 index 0000000..03a6904 --- /dev/null +++ b/cmd/api/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "os/signal" + "syscall" + "time" + + "blueprint_fix/internal/server" +) + +func gracefulShutdown(apiServer *http.Server, done chan bool) { + // Create context that listens for the interrupt signal from the OS. + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + // Listen for the interrupt signal. + <-ctx.Done() + + log.Println("shutting down gracefully, press Ctrl+C again to force") + + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := apiServer.Shutdown(ctx); err != nil { + log.Printf("Server forced to shutdown with error: %v", err) + } + + log.Println("Server exiting") + + // Notify the main goroutine that the shutdown is complete + done <- true +} + +func main() { + + server := server.NewServer() + + // Create a done channel to signal when the shutdown is complete + done := make(chan bool, 1) + + // Run graceful shutdown in a separate goroutine + go gracefulShutdown(server, done) + + err := server.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + panic(fmt.Sprintf("http server error: %s", err)) + } + + // Wait for the graceful shutdown to complete + <-done + log.Println("Graceful shutdown complete.") +} diff --git a/cmd/web/assets/css/input.css b/cmd/web/assets/css/input.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/cmd/web/assets/css/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/cmd/web/assets/css/output.css b/cmd/web/assets/css/output.css new file mode 100644 index 0000000..e69de29 diff --git a/cmd/web/assets/js/htmx.min.js b/cmd/web/assets/js/htmx.min.js new file mode 100644 index 0000000..c909e02 --- /dev/null +++ b/cmd/web/assets/js/htmx.min.js @@ -0,0 +1,3476 @@ +var htmx = (function () { + "use strict"; + const Q = { + onLoad: null, + process: null, + on: null, + off: null, + trigger: null, + ajax: null, + find: null, + findAll: null, + closest: null, + values: function (e, t) { + const n = cn(e, t || "post"); + return n.values; + }, + remove: null, + addClass: null, + removeClass: null, + toggleClass: null, + takeClass: null, + swap: null, + defineExtension: null, + removeExtension: null, + logAll: null, + logNone: null, + logger: null, + config: { + historyEnabled: true, + historyCacheSize: 10, + refreshOnHistoryMiss: false, + defaultSwapStyle: "innerHTML", + defaultSwapDelay: 0, + defaultSettleDelay: 20, + includeIndicatorStyles: true, + indicatorClass: "htmx-indicator", + requestClass: "htmx-request", + addedClass: "htmx-added", + settlingClass: "htmx-settling", + swappingClass: "htmx-swapping", + allowEval: true, + allowScriptTags: true, + inlineScriptNonce: "", + inlineStyleNonce: "", + attributesToSettle: ["class", "style", "width", "height"], + withCredentials: false, + timeout: 0, + wsReconnectDelay: "full-jitter", + wsBinaryType: "blob", + disableSelector: "[hx-disable], [data-hx-disable]", + scrollBehavior: "instant", + defaultFocusScroll: false, + getCacheBusterParam: false, + globalViewTransitions: false, + methodsThatUseUrlParams: ["get", "delete"], + selfRequestsOnly: true, + ignoreTitle: false, + scrollIntoViewOnBoost: true, + triggerSpecsCache: null, + disableInheritance: false, + responseHandling: [ + { code: "204", swap: false }, + { code: "[23]..", swap: true }, + { code: "[45]..", swap: false, error: true }, + ], + allowNestedOobSwaps: true, + }, + parseInterval: null, + _: null, + version: "2.0.3", + }; + Q.onLoad = j; + Q.process = kt; + Q.on = ye; + Q.off = be; + Q.trigger = de; + Q.ajax = Rn; + Q.find = r; + Q.findAll = x; + Q.closest = g; + Q.remove = z; + Q.addClass = K; + Q.removeClass = G; + Q.toggleClass = W; + Q.takeClass = Z; + Q.swap = $e; + Q.defineExtension = Fn; + Q.removeExtension = Bn; + Q.logAll = V; + Q.logNone = _; + Q.parseInterval = h; + Q._ = e; + const n = { + addTriggerHandler: St, + bodyContains: le, + canAccessLocalStorage: B, + findThisElement: Se, + filterValues: dn, + swap: $e, + hasAttribute: s, + getAttributeValue: te, + getClosestAttributeValue: re, + getClosestMatch: i, + getExpressionVars: En, + getHeaders: fn, + getInputValues: cn, + getInternalData: ie, + getSwapSpecification: gn, + getTriggerSpecs: st, + getTarget: Ee, + makeFragment: P, + mergeObjects: ce, + makeSettleInfo: xn, + oobSwap: He, + querySelectorExt: ae, + settleImmediately: Kt, + shouldCancel: dt, + triggerEvent: de, + triggerErrorEvent: fe, + withExtensions: Ft, + }; + const o = ["get", "post", "put", "delete", "patch"]; + const R = o + .map(function (e) { + return "[hx-" + e + "], [data-hx-" + e + "]"; + }) + .join(", "); + function h(e) { + if (e == undefined) { + return undefined; + } + let t = NaN; + if (e.slice(-2) == "ms") { + t = parseFloat(e.slice(0, -2)); + } else if (e.slice(-1) == "s") { + t = parseFloat(e.slice(0, -1)) * 1e3; + } else if (e.slice(-1) == "m") { + t = parseFloat(e.slice(0, -1)) * 1e3 * 60; + } else { + t = parseFloat(e); + } + return isNaN(t) ? undefined : t; + } + function ee(e, t) { + return e instanceof Element && e.getAttribute(t); + } + function s(e, t) { + return ( + !!e.hasAttribute && (e.hasAttribute(t) || e.hasAttribute("data-" + t)) + ); + } + function te(e, t) { + return ee(e, t) || ee(e, "data-" + t); + } + function c(e) { + const t = e.parentElement; + if (!t && e.parentNode instanceof ShadowRoot) return e.parentNode; + return t; + } + function ne() { + return document; + } + function m(e, t) { + return e.getRootNode ? e.getRootNode({ composed: t }) : ne(); + } + function i(e, t) { + while (e && !t(e)) { + e = c(e); + } + return e || null; + } + function H(e, t, n) { + const r = te(t, n); + const o = te(t, "hx-disinherit"); + var i = te(t, "hx-inherit"); + if (e !== t) { + if (Q.config.disableInheritance) { + if (i && (i === "*" || i.split(" ").indexOf(n) >= 0)) { + return r; + } else { + return null; + } + } + if (o && (o === "*" || o.split(" ").indexOf(n) >= 0)) { + return "unset"; + } + } + return r; + } + function re(t, n) { + let r = null; + i(t, function (e) { + return !!(r = H(t, ue(e), n)); + }); + if (r !== "unset") { + return r; + } + } + function d(e, t) { + const n = + e instanceof Element && + (e.matches || + e.matchesSelector || + e.msMatchesSelector || + e.mozMatchesSelector || + e.webkitMatchesSelector || + e.oMatchesSelector); + return !!n && n.call(e, t); + } + function T(e) { + const t = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i; + const n = t.exec(e); + if (n) { + return n[1].toLowerCase(); + } else { + return ""; + } + } + function q(e) { + const t = new DOMParser(); + return t.parseFromString(e, "text/html"); + } + function L(e, t) { + while (t.childNodes.length > 0) { + e.append(t.childNodes[0]); + } + } + function N(e) { + const t = ne().createElement("script"); + se(e.attributes, function (e) { + t.setAttribute(e.name, e.value); + }); + t.textContent = e.textContent; + t.async = false; + if (Q.config.inlineScriptNonce) { + t.nonce = Q.config.inlineScriptNonce; + } + return t; + } + function A(e) { + return ( + e.matches("script") && + (e.type === "text/javascript" || e.type === "module" || e.type === "") + ); + } + function I(e) { + Array.from(e.querySelectorAll("script")).forEach((e) => { + if (A(e)) { + const t = N(e); + const n = e.parentNode; + try { + n.insertBefore(t, e); + } catch (e) { + C(e); + } finally { + e.remove(); + } + } + }); + } + function P(e) { + const t = e.replace(/]*)?>[\s\S]*?<\/head>/i, ""); + const n = T(t); + let r; + if (n === "html") { + r = new DocumentFragment(); + const i = q(e); + L(r, i.body); + r.title = i.title; + } else if (n === "body") { + r = new DocumentFragment(); + const i = q(t); + L(r, i.body); + r.title = i.title; + } else { + const i = q( + '", + ); + r = i.querySelector("template").content; + r.title = i.title; + var o = r.querySelector("title"); + if (o && o.parentNode === r) { + o.remove(); + r.title = o.innerText; + } + } + if (r) { + if (Q.config.allowScriptTags) { + I(r); + } else { + r.querySelectorAll("script").forEach((e) => e.remove()); + } + } + return r; + } + function oe(e) { + if (e) { + e(); + } + } + function t(e, t) { + return Object.prototype.toString.call(e) === "[object " + t + "]"; + } + function k(e) { + return typeof e === "function"; + } + function D(e) { + return t(e, "Object"); + } + function ie(e) { + const t = "htmx-internal-data"; + let n = e[t]; + if (!n) { + n = e[t] = {}; + } + return n; + } + function M(t) { + const n = []; + if (t) { + for (let e = 0; e < t.length; e++) { + n.push(t[e]); + } + } + return n; + } + function se(t, n) { + if (t) { + for (let e = 0; e < t.length; e++) { + n(t[e]); + } + } + } + function X(e) { + const t = e.getBoundingClientRect(); + const n = t.top; + const r = t.bottom; + return n < window.innerHeight && r >= 0; + } + function le(e) { + const t = e.getRootNode && e.getRootNode(); + if (t && t instanceof window.ShadowRoot) { + return ne().body.contains(t.host); + } else { + return ne().body.contains(e); + } + } + function F(e) { + return e.trim().split(/\s+/); + } + function ce(e, t) { + for (const n in t) { + if (t.hasOwnProperty(n)) { + e[n] = t[n]; + } + } + return e; + } + function S(e) { + try { + return JSON.parse(e); + } catch (e) { + C(e); + return null; + } + } + function B() { + const e = "htmx:localStorageTest"; + try { + localStorage.setItem(e, e); + localStorage.removeItem(e); + return true; + } catch (e) { + return false; + } + } + function U(t) { + try { + const e = new URL(t); + if (e) { + t = e.pathname + e.search; + } + if (!/^\/$/.test(t)) { + t = t.replace(/\/+$/, ""); + } + return t; + } catch (e) { + return t; + } + } + function e(e) { + return vn(ne().body, function () { + return eval(e); + }); + } + function j(t) { + const e = Q.on("htmx:load", function (e) { + t(e.detail.elt); + }); + return e; + } + function V() { + Q.logger = function (e, t, n) { + if (console) { + console.log(t, e, n); + } + }; + } + function _() { + Q.logger = null; + } + function r(e, t) { + if (typeof e !== "string") { + return e.querySelector(t); + } else { + return r(ne(), e); + } + } + function x(e, t) { + if (typeof e !== "string") { + return e.querySelectorAll(t); + } else { + return x(ne(), e); + } + } + function E() { + return window; + } + function z(e, t) { + e = y(e); + if (t) { + E().setTimeout(function () { + z(e); + e = null; + }, t); + } else { + c(e).removeChild(e); + } + } + function ue(e) { + return e instanceof Element ? e : null; + } + function $(e) { + return e instanceof HTMLElement ? e : null; + } + function J(e) { + return typeof e === "string" ? e : null; + } + function f(e) { + return e instanceof Element || + e instanceof Document || + e instanceof DocumentFragment + ? e + : null; + } + function K(e, t, n) { + e = ue(y(e)); + if (!e) { + return; + } + if (n) { + E().setTimeout(function () { + K(e, t); + e = null; + }, n); + } else { + e.classList && e.classList.add(t); + } + } + function G(e, t, n) { + let r = ue(y(e)); + if (!r) { + return; + } + if (n) { + E().setTimeout(function () { + G(r, t); + r = null; + }, n); + } else { + if (r.classList) { + r.classList.remove(t); + if (r.classList.length === 0) { + r.removeAttribute("class"); + } + } + } + } + function W(e, t) { + e = y(e); + e.classList.toggle(t); + } + function Z(e, t) { + e = y(e); + se(e.parentElement.children, function (e) { + G(e, t); + }); + K(ue(e), t); + } + function g(e, t) { + e = ue(y(e)); + if (e && e.closest) { + return e.closest(t); + } else { + do { + if (e == null || d(e, t)) { + return e; + } + } while ((e = e && ue(c(e)))); + return null; + } + } + function l(e, t) { + return e.substring(0, t.length) === t; + } + function Y(e, t) { + return e.substring(e.length - t.length) === t; + } + function ge(e) { + const t = e.trim(); + if (l(t, "<") && Y(t, "/>")) { + return t.substring(1, t.length - 2); + } else { + return t; + } + } + function p(e, t, n) { + e = y(e); + if (t.indexOf("closest ") === 0) { + return [g(ue(e), ge(t.substr(8)))]; + } else if (t.indexOf("find ") === 0) { + return [r(f(e), ge(t.substr(5)))]; + } else if (t === "next") { + return [ue(e).nextElementSibling]; + } else if (t.indexOf("next ") === 0) { + return [pe(e, ge(t.substr(5)), !!n)]; + } else if (t === "previous") { + return [ue(e).previousElementSibling]; + } else if (t.indexOf("previous ") === 0) { + return [me(e, ge(t.substr(9)), !!n)]; + } else if (t === "document") { + return [document]; + } else if (t === "window") { + return [window]; + } else if (t === "body") { + return [document.body]; + } else if (t === "root") { + return [m(e, !!n)]; + } else if (t === "host") { + return [e.getRootNode().host]; + } else if (t.indexOf("global ") === 0) { + return p(e, t.slice(7), true); + } else { + return M(f(m(e, !!n)).querySelectorAll(ge(t))); + } + } + var pe = function (t, e, n) { + const r = f(m(t, n)).querySelectorAll(e); + for (let e = 0; e < r.length; e++) { + const o = r[e]; + if (o.compareDocumentPosition(t) === Node.DOCUMENT_POSITION_PRECEDING) { + return o; + } + } + }; + var me = function (t, e, n) { + const r = f(m(t, n)).querySelectorAll(e); + for (let e = r.length - 1; e >= 0; e--) { + const o = r[e]; + if (o.compareDocumentPosition(t) === Node.DOCUMENT_POSITION_FOLLOWING) { + return o; + } + } + }; + function ae(e, t) { + if (typeof e !== "string") { + return p(e, t)[0]; + } else { + return p(ne().body, e)[0]; + } + } + function y(e, t) { + if (typeof e === "string") { + return r(f(t) || document, e); + } else { + return e; + } + } + function xe(e, t, n, r) { + if (k(t)) { + return { target: ne().body, event: J(e), listener: t, options: n }; + } else { + return { target: y(e), event: J(t), listener: n, options: r }; + } + } + function ye(t, n, r, o) { + Vn(function () { + const e = xe(t, n, r, o); + e.target.addEventListener(e.event, e.listener, e.options); + }); + const e = k(n); + return e ? n : r; + } + function be(t, n, r) { + Vn(function () { + const e = xe(t, n, r); + e.target.removeEventListener(e.event, e.listener); + }); + return k(n) ? n : r; + } + const ve = ne().createElement("output"); + function we(e, t) { + const n = re(e, t); + if (n) { + if (n === "this") { + return [Se(e, t)]; + } else { + const r = p(e, n); + if (r.length === 0) { + C('The selector "' + n + '" on ' + t + " returned no matches!"); + return [ve]; + } else { + return r; + } + } + } + } + function Se(e, t) { + return ue( + i(e, function (e) { + return te(ue(e), t) != null; + }), + ); + } + function Ee(e) { + const t = re(e, "hx-target"); + if (t) { + if (t === "this") { + return Se(e, "hx-target"); + } else { + return ae(e, t); + } + } else { + const n = ie(e); + if (n.boosted) { + return ne().body; + } else { + return e; + } + } + } + function Ce(t) { + const n = Q.config.attributesToSettle; + for (let e = 0; e < n.length; e++) { + if (t === n[e]) { + return true; + } + } + return false; + } + function Oe(t, n) { + se(t.attributes, function (e) { + if (!n.hasAttribute(e.name) && Ce(e.name)) { + t.removeAttribute(e.name); + } + }); + se(n.attributes, function (e) { + if (Ce(e.name)) { + t.setAttribute(e.name, e.value); + } + }); + } + function Re(t, e) { + const n = Un(e); + for (let e = 0; e < n.length; e++) { + const r = n[e]; + try { + if (r.isInlineSwap(t)) { + return true; + } + } catch (e) { + C(e); + } + } + return t === "outerHTML"; + } + function He(e, o, i, t) { + t = t || ne(); + let n = "#" + ee(o, "id"); + let s = "outerHTML"; + if (e === "true") { + } else if (e.indexOf(":") > 0) { + s = e.substr(0, e.indexOf(":")); + n = e.substr(e.indexOf(":") + 1, e.length); + } else { + s = e; + } + o.removeAttribute("hx-swap-oob"); + o.removeAttribute("data-hx-swap-oob"); + const r = p(t, n, false); + if (r) { + se(r, function (e) { + let t; + const n = o.cloneNode(true); + t = ne().createDocumentFragment(); + t.appendChild(n); + if (!Re(s, e)) { + t = f(n); + } + const r = { shouldSwap: true, target: e, fragment: t }; + if (!de(e, "htmx:oobBeforeSwap", r)) return; + e = r.target; + if (r.shouldSwap) { + qe(t); + _e(s, e, e, t, i); + Te(); + } + se(i.elts, function (e) { + de(e, "htmx:oobAfterSwap", r); + }); + }); + o.parentNode.removeChild(o); + } else { + o.parentNode.removeChild(o); + fe(ne().body, "htmx:oobErrorNoTarget", { content: o }); + } + return e; + } + function Te() { + const e = r("#--htmx-preserve-pantry--"); + if (e) { + for (const t of [...e.children]) { + const n = r("#" + t.id); + n.parentNode.moveBefore(t, n); + n.remove(); + } + e.remove(); + } + } + function qe(e) { + se(x(e, "[hx-preserve], [data-hx-preserve]"), function (e) { + const t = te(e, "id"); + const n = ne().getElementById(t); + if (n != null) { + if (e.moveBefore) { + let e = r("#--htmx-preserve-pantry--"); + if (e == null) { + ne().body.insertAdjacentHTML( + "afterend", + "
", + ); + e = r("#--htmx-preserve-pantry--"); + } + e.moveBefore(n, null); + } else { + e.parentNode.replaceChild(n, e); + } + } + }); + } + function Le(l, e, c) { + se(e.querySelectorAll("[id]"), function (t) { + const n = ee(t, "id"); + if (n && n.length > 0) { + const r = n.replace("'", "\\'"); + const o = t.tagName.replace(":", "\\:"); + const e = f(l); + const i = e && e.querySelector(o + "[id='" + r + "']"); + if (i && i !== e) { + const s = t.cloneNode(); + Oe(t, i); + c.tasks.push(function () { + Oe(t, s); + }); + } + } + }); + } + function Ne(e) { + return function () { + G(e, Q.config.addedClass); + kt(ue(e)); + Ae(f(e)); + de(e, "htmx:load"); + }; + } + function Ae(e) { + const t = "[autofocus]"; + const n = $(d(e, t) ? e : e.querySelector(t)); + if (n != null) { + n.focus(); + } + } + function u(e, t, n, r) { + Le(e, n, r); + while (n.childNodes.length > 0) { + const o = n.firstChild; + K(ue(o), Q.config.addedClass); + e.insertBefore(o, t); + if (o.nodeType !== Node.TEXT_NODE && o.nodeType !== Node.COMMENT_NODE) { + r.tasks.push(Ne(o)); + } + } + } + function Ie(e, t) { + let n = 0; + while (n < e.length) { + t = ((t << 5) - t + e.charCodeAt(n++)) | 0; + } + return t; + } + function Pe(t) { + let n = 0; + if (t.attributes) { + for (let e = 0; e < t.attributes.length; e++) { + const r = t.attributes[e]; + if (r.value) { + n = Ie(r.name, n); + n = Ie(r.value, n); + } + } + } + return n; + } + function ke(t) { + const n = ie(t); + if (n.onHandlers) { + for (let e = 0; e < n.onHandlers.length; e++) { + const r = n.onHandlers[e]; + be(t, r.event, r.listener); + } + delete n.onHandlers; + } + } + function De(e) { + const t = ie(e); + if (t.timeout) { + clearTimeout(t.timeout); + } + if (t.listenerInfos) { + se(t.listenerInfos, function (e) { + if (e.on) { + be(e.on, e.trigger, e.listener); + } + }); + } + ke(e); + se(Object.keys(t), function (e) { + delete t[e]; + }); + } + function a(e) { + de(e, "htmx:beforeCleanupElement"); + De(e); + if (e.children) { + se(e.children, function (e) { + a(e); + }); + } + } + function Me(t, e, n) { + if (t instanceof Element && t.tagName === "BODY") { + return Ve(t, e, n); + } + let r; + const o = t.previousSibling; + const i = c(t); + if (!i) { + return; + } + u(i, t, e, n); + if (o == null) { + r = i.firstChild; + } else { + r = o.nextSibling; + } + n.elts = n.elts.filter(function (e) { + return e !== t; + }); + while (r && r !== t) { + if (r instanceof Element) { + n.elts.push(r); + } + r = r.nextSibling; + } + a(t); + if (t instanceof Element) { + t.remove(); + } else { + t.parentNode.removeChild(t); + } + } + function Xe(e, t, n) { + return u(e, e.firstChild, t, n); + } + function Fe(e, t, n) { + return u(c(e), e, t, n); + } + function Be(e, t, n) { + return u(e, null, t, n); + } + function Ue(e, t, n) { + return u(c(e), e.nextSibling, t, n); + } + function je(e) { + a(e); + const t = c(e); + if (t) { + return t.removeChild(e); + } + } + function Ve(e, t, n) { + const r = e.firstChild; + u(e, r, t, n); + if (r) { + while (r.nextSibling) { + a(r.nextSibling); + e.removeChild(r.nextSibling); + } + a(r); + e.removeChild(r); + } + } + function _e(t, e, n, r, o) { + switch (t) { + case "none": + return; + case "outerHTML": + Me(n, r, o); + return; + case "afterbegin": + Xe(n, r, o); + return; + case "beforebegin": + Fe(n, r, o); + return; + case "beforeend": + Be(n, r, o); + return; + case "afterend": + Ue(n, r, o); + return; + case "delete": + je(n); + return; + default: + var i = Un(e); + for (let e = 0; e < i.length; e++) { + const s = i[e]; + try { + const l = s.handleSwap(t, n, r, o); + if (l) { + if (Array.isArray(l)) { + for (let e = 0; e < l.length; e++) { + const c = l[e]; + if ( + c.nodeType !== Node.TEXT_NODE && + c.nodeType !== Node.COMMENT_NODE + ) { + o.tasks.push(Ne(c)); + } + } + } + return; + } + } catch (e) { + C(e); + } + } + if (t === "innerHTML") { + Ve(n, r, o); + } else { + _e(Q.config.defaultSwapStyle, e, n, r, o); + } + } + } + function ze(e, n, r) { + var t = x(e, "[hx-swap-oob], [data-hx-swap-oob]"); + se(t, function (e) { + if (Q.config.allowNestedOobSwaps || e.parentElement === null) { + const t = te(e, "hx-swap-oob"); + if (t != null) { + He(t, e, n, r); + } + } else { + e.removeAttribute("hx-swap-oob"); + e.removeAttribute("data-hx-swap-oob"); + } + }); + return t.length > 0; + } + function $e(e, t, r, o) { + if (!o) { + o = {}; + } + e = y(e); + const i = o.contextElement ? m(o.contextElement, false) : ne(); + const n = document.activeElement; + let s = {}; + try { + s = { + elt: n, + start: n ? n.selectionStart : null, + end: n ? n.selectionEnd : null, + }; + } catch (e) {} + const l = xn(e); + if (r.swapStyle === "textContent") { + e.textContent = t; + } else { + let n = P(t); + l.title = n.title; + if (o.selectOOB) { + const u = o.selectOOB.split(","); + for (let t = 0; t < u.length; t++) { + const a = u[t].split(":", 2); + let e = a[0].trim(); + if (e.indexOf("#") === 0) { + e = e.substring(1); + } + const f = a[1] || "true"; + const d = n.querySelector("#" + e); + if (d) { + He(f, d, l, i); + } + } + } + ze(n, l, i); + se(x(n, "template"), function (e) { + if (ze(e.content, l, i)) { + e.remove(); + } + }); + if (o.select) { + const h = ne().createDocumentFragment(); + se(n.querySelectorAll(o.select), function (e) { + h.appendChild(e); + }); + n = h; + } + qe(n); + _e(r.swapStyle, o.contextElement, e, n, l); + Te(); + } + if (s.elt && !le(s.elt) && ee(s.elt, "id")) { + const g = document.getElementById(ee(s.elt, "id")); + const p = { + preventScroll: + r.focusScroll !== undefined + ? !r.focusScroll + : !Q.config.defaultFocusScroll, + }; + if (g) { + if (s.start && g.setSelectionRange) { + try { + g.setSelectionRange(s.start, s.end); + } catch (e) {} + } + g.focus(p); + } + } + e.classList.remove(Q.config.swappingClass); + se(l.elts, function (e) { + if (e.classList) { + e.classList.add(Q.config.settlingClass); + } + de(e, "htmx:afterSwap", o.eventInfo); + }); + if (o.afterSwapCallback) { + o.afterSwapCallback(); + } + if (!r.ignoreTitle) { + kn(l.title); + } + const c = function () { + se(l.tasks, function (e) { + e.call(); + }); + se(l.elts, function (e) { + if (e.classList) { + e.classList.remove(Q.config.settlingClass); + } + de(e, "htmx:afterSettle", o.eventInfo); + }); + if (o.anchor) { + const e = ue(y("#" + o.anchor)); + if (e) { + e.scrollIntoView({ block: "start", behavior: "auto" }); + } + } + yn(l.elts, r); + if (o.afterSettleCallback) { + o.afterSettleCallback(); + } + }; + if (r.settleDelay > 0) { + E().setTimeout(c, r.settleDelay); + } else { + c(); + } + } + function Je(e, t, n) { + const r = e.getResponseHeader(t); + if (r.indexOf("{") === 0) { + const o = S(r); + for (const i in o) { + if (o.hasOwnProperty(i)) { + let e = o[i]; + if (D(e)) { + n = e.target !== undefined ? e.target : n; + } else { + e = { value: e }; + } + de(n, i, e); + } + } + } else { + const s = r.split(","); + for (let e = 0; e < s.length; e++) { + de(n, s[e].trim(), []); + } + } + } + const Ke = /\s/; + const b = /[\s,]/; + const Ge = /[_$a-zA-Z]/; + const We = /[_$a-zA-Z0-9]/; + const Ze = ['"', "'", "/"]; + const v = /[^\s]/; + const Ye = /[{(]/; + const Qe = /[})]/; + function et(e) { + const t = []; + let n = 0; + while (n < e.length) { + if (Ge.exec(e.charAt(n))) { + var r = n; + while (We.exec(e.charAt(n + 1))) { + n++; + } + t.push(e.substr(r, n - r + 1)); + } else if (Ze.indexOf(e.charAt(n)) !== -1) { + const o = e.charAt(n); + var r = n; + n++; + while (n < e.length && e.charAt(n) !== o) { + if (e.charAt(n) === "\\") { + n++; + } + n++; + } + t.push(e.substr(r, n - r + 1)); + } else { + const i = e.charAt(n); + t.push(i); + } + n++; + } + return t; + } + function tt(e, t, n) { + return ( + Ge.exec(e.charAt(0)) && + e !== "true" && + e !== "false" && + e !== "this" && + e !== n && + t !== "." + ); + } + function nt(r, o, i) { + if (o[0] === "[") { + o.shift(); + let e = 1; + let t = " return (function(" + i + "){ return ("; + let n = null; + while (o.length > 0) { + const s = o[0]; + if (s === "]") { + e--; + if (e === 0) { + if (n === null) { + t = t + "true"; + } + o.shift(); + t += ")})"; + try { + const l = vn( + r, + function () { + return Function(t)(); + }, + function () { + return true; + }, + ); + l.source = t; + return l; + } catch (e) { + fe(ne().body, "htmx:syntax:error", { error: e, source: t }); + return null; + } + } + } else if (s === "[") { + e++; + } + if (tt(s, n, i)) { + t += + "((" + + i + + "." + + s + + ") ? (" + + i + + "." + + s + + ") : (window." + + s + + "))"; + } else { + t = t + s; + } + n = o.shift(); + } + } + } + function w(e, t) { + let n = ""; + while (e.length > 0 && !t.test(e[0])) { + n += e.shift(); + } + return n; + } + function rt(e) { + let t; + if (e.length > 0 && Ye.test(e[0])) { + e.shift(); + t = w(e, Qe).trim(); + e.shift(); + } else { + t = w(e, b); + } + return t; + } + const ot = "input, textarea, select"; + function it(e, t, n) { + const r = []; + const o = et(t); + do { + w(o, v); + const l = o.length; + const c = w(o, /[,\[\s]/); + if (c !== "") { + if (c === "every") { + const u = { trigger: "every" }; + w(o, v); + u.pollInterval = h(w(o, /[,\[\s]/)); + w(o, v); + var i = nt(e, o, "event"); + if (i) { + u.eventFilter = i; + } + r.push(u); + } else { + const a = { trigger: c }; + var i = nt(e, o, "event"); + if (i) { + a.eventFilter = i; + } + w(o, v); + while (o.length > 0 && o[0] !== ",") { + const f = o.shift(); + if (f === "changed") { + a.changed = true; + } else if (f === "once") { + a.once = true; + } else if (f === "consume") { + a.consume = true; + } else if (f === "delay" && o[0] === ":") { + o.shift(); + a.delay = h(w(o, b)); + } else if (f === "from" && o[0] === ":") { + o.shift(); + if (Ye.test(o[0])) { + var s = rt(o); + } else { + var s = w(o, b); + if ( + s === "closest" || + s === "find" || + s === "next" || + s === "previous" + ) { + o.shift(); + const d = rt(o); + if (d.length > 0) { + s += " " + d; + } + } + } + a.from = s; + } else if (f === "target" && o[0] === ":") { + o.shift(); + a.target = rt(o); + } else if (f === "throttle" && o[0] === ":") { + o.shift(); + a.throttle = h(w(o, b)); + } else if (f === "queue" && o[0] === ":") { + o.shift(); + a.queue = w(o, b); + } else if (f === "root" && o[0] === ":") { + o.shift(); + a[f] = rt(o); + } else if (f === "threshold" && o[0] === ":") { + o.shift(); + a[f] = w(o, b); + } else { + fe(e, "htmx:syntax:error", { token: o.shift() }); + } + w(o, v); + } + r.push(a); + } + } + if (o.length === l) { + fe(e, "htmx:syntax:error", { token: o.shift() }); + } + w(o, v); + } while (o[0] === "," && o.shift()); + if (n) { + n[t] = r; + } + return r; + } + function st(e) { + const t = te(e, "hx-trigger"); + let n = []; + if (t) { + const r = Q.config.triggerSpecsCache; + n = (r && r[t]) || it(e, t, r); + } + if (n.length > 0) { + return n; + } else if (d(e, "form")) { + return [{ trigger: "submit" }]; + } else if (d(e, 'input[type="button"], input[type="submit"]')) { + return [{ trigger: "click" }]; + } else if (d(e, ot)) { + return [{ trigger: "change" }]; + } else { + return [{ trigger: "click" }]; + } + } + function lt(e) { + ie(e).cancelled = true; + } + function ct(e, t, n) { + const r = ie(e); + r.timeout = E().setTimeout(function () { + if (le(e) && r.cancelled !== true) { + if (!gt(n, e, Mt("hx:poll:trigger", { triggerSpec: n, target: e }))) { + t(e); + } + ct(e, t, n); + } + }, n.pollInterval); + } + function ut(e) { + return ( + location.hostname === e.hostname && + ee(e, "href") && + ee(e, "href").indexOf("#") !== 0 + ); + } + function at(e) { + return g(e, Q.config.disableSelector); + } + function ft(t, n, e) { + if ( + (t instanceof HTMLAnchorElement && + ut(t) && + (t.target === "" || t.target === "_self")) || + (t.tagName === "FORM" && + String(ee(t, "method")).toLowerCase() !== "dialog") + ) { + n.boosted = true; + let r, o; + if (t.tagName === "A") { + r = "get"; + o = ee(t, "href"); + } else { + const i = ee(t, "method"); + r = i ? i.toLowerCase() : "get"; + o = ee(t, "action"); + if (r === "get" && o.includes("?")) { + o = o.replace(/\?[^#]+/, ""); + } + } + e.forEach(function (e) { + pt( + t, + function (e, t) { + const n = ue(e); + if (at(n)) { + a(n); + return; + } + he(r, o, n, t); + }, + n, + e, + true, + ); + }); + } + } + function dt(e, t) { + const n = ue(t); + if (!n) { + return false; + } + if (e.type === "submit" || e.type === "click") { + if (n.tagName === "FORM") { + return true; + } + if (d(n, 'input[type="submit"], button') && g(n, "form") !== null) { + return true; + } + if ( + n instanceof HTMLAnchorElement && + n.href && + (n.getAttribute("href") === "#" || + n.getAttribute("href").indexOf("#") !== 0) + ) { + return true; + } + } + return false; + } + function ht(e, t) { + return ( + ie(e).boosted && + e instanceof HTMLAnchorElement && + t.type === "click" && + (t.ctrlKey || t.metaKey) + ); + } + function gt(e, t, n) { + const r = e.eventFilter; + if (r) { + try { + return r.call(t, n) !== true; + } catch (e) { + const o = r.source; + fe(ne().body, "htmx:eventFilter:error", { error: e, source: o }); + return true; + } + } + return false; + } + function pt(l, c, e, u, a) { + const f = ie(l); + let t; + if (u.from) { + t = p(l, u.from); + } else { + t = [l]; + } + if (u.changed) { + if (!("lastValue" in f)) { + f.lastValue = new WeakMap(); + } + t.forEach(function (e) { + if (!f.lastValue.has(u)) { + f.lastValue.set(u, new WeakMap()); + } + f.lastValue.get(u).set(e, e.value); + }); + } + se(t, function (i) { + const s = function (e) { + if (!le(l)) { + i.removeEventListener(u.trigger, s); + return; + } + if (ht(l, e)) { + return; + } + if (a || dt(e, l)) { + e.preventDefault(); + } + if (gt(u, l, e)) { + return; + } + const t = ie(e); + t.triggerSpec = u; + if (t.handledFor == null) { + t.handledFor = []; + } + if (t.handledFor.indexOf(l) < 0) { + t.handledFor.push(l); + if (u.consume) { + e.stopPropagation(); + } + if (u.target && e.target) { + if (!d(ue(e.target), u.target)) { + return; + } + } + if (u.once) { + if (f.triggeredOnce) { + return; + } else { + f.triggeredOnce = true; + } + } + if (u.changed) { + const n = event.target; + const r = n.value; + const o = f.lastValue.get(u); + if (o.has(n) && o.get(n) === r) { + return; + } + o.set(n, r); + } + if (f.delayed) { + clearTimeout(f.delayed); + } + if (f.throttle) { + return; + } + if (u.throttle > 0) { + if (!f.throttle) { + de(l, "htmx:trigger"); + c(l, e); + f.throttle = E().setTimeout(function () { + f.throttle = null; + }, u.throttle); + } + } else if (u.delay > 0) { + f.delayed = E().setTimeout(function () { + de(l, "htmx:trigger"); + c(l, e); + }, u.delay); + } else { + de(l, "htmx:trigger"); + c(l, e); + } + } + }; + if (e.listenerInfos == null) { + e.listenerInfos = []; + } + e.listenerInfos.push({ trigger: u.trigger, listener: s, on: i }); + i.addEventListener(u.trigger, s); + }); + } + let mt = false; + let xt = null; + function yt() { + if (!xt) { + xt = function () { + mt = true; + }; + window.addEventListener("scroll", xt); + window.addEventListener("resize", xt); + setInterval(function () { + if (mt) { + mt = false; + se( + ne().querySelectorAll( + "[hx-trigger*='revealed'],[data-hx-trigger*='revealed']", + ), + function (e) { + bt(e); + }, + ); + } + }, 200); + } + } + function bt(e) { + if (!s(e, "data-hx-revealed") && X(e)) { + e.setAttribute("data-hx-revealed", "true"); + const t = ie(e); + if (t.initHash) { + de(e, "revealed"); + } else { + e.addEventListener( + "htmx:afterProcessNode", + function () { + de(e, "revealed"); + }, + { once: true }, + ); + } + } + } + function vt(e, t, n, r) { + const o = function () { + if (!n.loaded) { + n.loaded = true; + t(e); + } + }; + if (r > 0) { + E().setTimeout(o, r); + } else { + o(); + } + } + function wt(t, n, e) { + let i = false; + se(o, function (r) { + if (s(t, "hx-" + r)) { + const o = te(t, "hx-" + r); + i = true; + n.path = o; + n.verb = r; + e.forEach(function (e) { + St(t, e, n, function (e, t) { + const n = ue(e); + if (g(n, Q.config.disableSelector)) { + a(n); + return; + } + he(r, o, n, t); + }); + }); + } + }); + return i; + } + function St(r, e, t, n) { + if (e.trigger === "revealed") { + yt(); + pt(r, n, t, e); + bt(ue(r)); + } else if (e.trigger === "intersect") { + const o = {}; + if (e.root) { + o.root = ae(r, e.root); + } + if (e.threshold) { + o.threshold = parseFloat(e.threshold); + } + const i = new IntersectionObserver(function (t) { + for (let e = 0; e < t.length; e++) { + const n = t[e]; + if (n.isIntersecting) { + de(r, "intersect"); + break; + } + } + }, o); + i.observe(ue(r)); + pt(ue(r), n, t, e); + } else if (e.trigger === "load") { + if (!gt(e, r, Mt("load", { elt: r }))) { + vt(ue(r), n, t, e.delay); + } + } else if (e.pollInterval > 0) { + t.polling = true; + ct(ue(r), n, e); + } else { + pt(r, n, t, e); + } + } + function Et(e) { + const t = ue(e); + if (!t) { + return false; + } + const n = t.attributes; + for (let e = 0; e < n.length; e++) { + const r = n[e].name; + if ( + l(r, "hx-on:") || + l(r, "data-hx-on:") || + l(r, "hx-on-") || + l(r, "data-hx-on-") + ) { + return true; + } + } + return false; + } + const Ct = new XPathEvaluator().createExpression( + './/*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', + ); + function Ot(e, t) { + if (Et(e)) { + t.push(ue(e)); + } + const n = Ct.evaluate(e); + let r = null; + while ((r = n.iterateNext())) t.push(ue(r)); + } + function Rt(e) { + const t = []; + if (e instanceof DocumentFragment) { + for (const n of e.childNodes) { + Ot(n, t); + } + } else { + Ot(e, t); + } + return t; + } + function Ht(e) { + if (e.querySelectorAll) { + const n = + ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; + const r = []; + for (const i in Mn) { + const s = Mn[i]; + if (s.getSelectors) { + var t = s.getSelectors(); + if (t) { + r.push(t); + } + } + } + const o = e.querySelectorAll( + R + + n + + ", form, [type='submit']," + + " [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger]" + + r + .flat() + .map((e) => ", " + e) + .join(""), + ); + return o; + } else { + return []; + } + } + function Tt(e) { + const t = g(ue(e.target), "button, input[type='submit']"); + const n = Lt(e); + if (n) { + n.lastButtonClicked = t; + } + } + function qt(e) { + const t = Lt(e); + if (t) { + t.lastButtonClicked = null; + } + } + function Lt(e) { + const t = g(ue(e.target), "button, input[type='submit']"); + if (!t) { + return; + } + const n = y("#" + ee(t, "form"), t.getRootNode()) || g(t, "form"); + if (!n) { + return; + } + return ie(n); + } + function Nt(e) { + e.addEventListener("click", Tt); + e.addEventListener("focusin", Tt); + e.addEventListener("focusout", qt); + } + function At(t, e, n) { + const r = ie(t); + if (!Array.isArray(r.onHandlers)) { + r.onHandlers = []; + } + let o; + const i = function (e) { + vn(t, function () { + if (at(t)) { + return; + } + if (!o) { + o = new Function("event", n); + } + o.call(t, e); + }); + }; + t.addEventListener(e, i); + r.onHandlers.push({ event: e, listener: i }); + } + function It(t) { + ke(t); + for (let e = 0; e < t.attributes.length; e++) { + const n = t.attributes[e].name; + const r = t.attributes[e].value; + if (l(n, "hx-on") || l(n, "data-hx-on")) { + const o = n.indexOf("-on") + 3; + const i = n.slice(o, o + 1); + if (i === "-" || i === ":") { + let e = n.slice(o + 1); + if (l(e, ":")) { + e = "htmx" + e; + } else if (l(e, "-")) { + e = "htmx:" + e.slice(1); + } else if (l(e, "htmx-")) { + e = "htmx:" + e.slice(5); + } + At(t, e, r); + } + } + } + } + function Pt(t) { + if (g(t, Q.config.disableSelector)) { + a(t); + return; + } + const n = ie(t); + if (n.initHash !== Pe(t)) { + De(t); + n.initHash = Pe(t); + de(t, "htmx:beforeProcessNode"); + const e = st(t); + const r = wt(t, n, e); + if (!r) { + if (re(t, "hx-boost") === "true") { + ft(t, n, e); + } else if (s(t, "hx-trigger")) { + e.forEach(function (e) { + St(t, e, n, function () {}); + }); + } + } + if ( + t.tagName === "FORM" || + (ee(t, "type") === "submit" && s(t, "form")) + ) { + Nt(t); + } + de(t, "htmx:afterProcessNode"); + } + } + function kt(e) { + e = y(e); + if (g(e, Q.config.disableSelector)) { + a(e); + return; + } + Pt(e); + se(Ht(e), function (e) { + Pt(e); + }); + se(Rt(e), It); + } + function Dt(e) { + return e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); + } + function Mt(e, t) { + let n; + if (window.CustomEvent && typeof window.CustomEvent === "function") { + n = new CustomEvent(e, { + bubbles: true, + cancelable: true, + composed: true, + detail: t, + }); + } else { + n = ne().createEvent("CustomEvent"); + n.initCustomEvent(e, true, true, t); + } + return n; + } + function fe(e, t, n) { + de(e, t, ce({ error: t }, n)); + } + function Xt(e) { + return e === "htmx:afterProcessNode"; + } + function Ft(e, t) { + se(Un(e), function (e) { + try { + t(e); + } catch (e) { + C(e); + } + }); + } + function C(e) { + if (console.error) { + console.error(e); + } else if (console.log) { + console.log("ERROR: ", e); + } + } + function de(e, t, n) { + e = y(e); + if (n == null) { + n = {}; + } + n.elt = e; + const r = Mt(t, n); + if (Q.logger && !Xt(t)) { + Q.logger(e, t, n); + } + if (n.error) { + C(n.error); + de(e, "htmx:error", { errorInfo: n }); + } + let o = e.dispatchEvent(r); + const i = Dt(t); + if (o && i !== t) { + const s = Mt(i, r.detail); + o = o && e.dispatchEvent(s); + } + Ft(ue(e), function (e) { + o = o && e.onEvent(t, r) !== false && !r.defaultPrevented; + }); + return o; + } + let Bt = location.pathname + location.search; + function Ut() { + const e = ne().querySelector("[hx-history-elt],[data-hx-history-elt]"); + return e || ne().body; + } + function jt(t, e) { + if (!B()) { + return; + } + const n = _t(e); + const r = ne().title; + const o = window.scrollY; + if (Q.config.historyCacheSize <= 0) { + localStorage.removeItem("htmx-history-cache"); + return; + } + t = U(t); + const i = S(localStorage.getItem("htmx-history-cache")) || []; + for (let e = 0; e < i.length; e++) { + if (i[e].url === t) { + i.splice(e, 1); + break; + } + } + const s = { url: t, content: n, title: r, scroll: o }; + de(ne().body, "htmx:historyItemCreated", { item: s, cache: i }); + i.push(s); + while (i.length > Q.config.historyCacheSize) { + i.shift(); + } + while (i.length > 0) { + try { + localStorage.setItem("htmx-history-cache", JSON.stringify(i)); + break; + } catch (e) { + fe(ne().body, "htmx:historyCacheError", { cause: e, cache: i }); + i.shift(); + } + } + } + function Vt(t) { + if (!B()) { + return null; + } + t = U(t); + const n = S(localStorage.getItem("htmx-history-cache")) || []; + for (let e = 0; e < n.length; e++) { + if (n[e].url === t) { + return n[e]; + } + } + return null; + } + function _t(e) { + const t = Q.config.requestClass; + const n = e.cloneNode(true); + se(x(n, "." + t), function (e) { + G(e, t); + }); + se(x(n, "[data-disabled-by-htmx]"), function (e) { + e.removeAttribute("disabled"); + }); + return n.innerHTML; + } + function zt() { + const e = Ut(); + const t = Bt || location.pathname + location.search; + let n; + try { + n = ne().querySelector( + '[hx-history="false" i],[data-hx-history="false" i]', + ); + } catch (e) { + n = ne().querySelector('[hx-history="false"],[data-hx-history="false"]'); + } + if (!n) { + de(ne().body, "htmx:beforeHistorySave", { path: t, historyElt: e }); + jt(t, e); + } + if (Q.config.historyEnabled) + history.replaceState({ htmx: true }, ne().title, window.location.href); + } + function $t(e) { + if (Q.config.getCacheBusterParam) { + e = e.replace(/org\.htmx\.cache-buster=[^&]*&?/, ""); + if (Y(e, "&") || Y(e, "?")) { + e = e.slice(0, -1); + } + } + if (Q.config.historyEnabled) { + history.pushState({ htmx: true }, "", e); + } + Bt = e; + } + function Jt(e) { + if (Q.config.historyEnabled) history.replaceState({ htmx: true }, "", e); + Bt = e; + } + function Kt(e) { + se(e, function (e) { + e.call(undefined); + }); + } + function Gt(o) { + const e = new XMLHttpRequest(); + const i = { path: o, xhr: e }; + de(ne().body, "htmx:historyCacheMiss", i); + e.open("GET", o, true); + e.setRequestHeader("HX-Request", "true"); + e.setRequestHeader("HX-History-Restore-Request", "true"); + e.setRequestHeader("HX-Current-URL", ne().location.href); + e.onload = function () { + if (this.status >= 200 && this.status < 400) { + de(ne().body, "htmx:historyCacheMissLoad", i); + const e = P(this.response); + const t = + e.querySelector("[hx-history-elt],[data-hx-history-elt]") || e; + const n = Ut(); + const r = xn(n); + kn(e.title); + qe(e); + Ve(n, t, r); + Te(); + Kt(r.tasks); + Bt = o; + de(ne().body, "htmx:historyRestore", { + path: o, + cacheMiss: true, + serverResponse: this.response, + }); + } else { + fe(ne().body, "htmx:historyCacheMissLoadError", i); + } + }; + e.send(); + } + function Wt(e) { + zt(); + e = e || location.pathname + location.search; + const t = Vt(e); + if (t) { + const n = P(t.content); + const r = Ut(); + const o = xn(r); + kn(t.title); + qe(n); + Ve(r, n, o); + Te(); + Kt(o.tasks); + E().setTimeout(function () { + window.scrollTo(0, t.scroll); + }, 0); + Bt = e; + de(ne().body, "htmx:historyRestore", { path: e, item: t }); + } else { + if (Q.config.refreshOnHistoryMiss) { + window.location.reload(true); + } else { + Gt(e); + } + } + } + function Zt(e) { + let t = we(e, "hx-indicator"); + if (t == null) { + t = [e]; + } + se(t, function (e) { + const t = ie(e); + t.requestCount = (t.requestCount || 0) + 1; + e.classList.add.call(e.classList, Q.config.requestClass); + }); + return t; + } + function Yt(e) { + let t = we(e, "hx-disabled-elt"); + if (t == null) { + t = []; + } + se(t, function (e) { + const t = ie(e); + t.requestCount = (t.requestCount || 0) + 1; + e.setAttribute("disabled", ""); + e.setAttribute("data-disabled-by-htmx", ""); + }); + return t; + } + function Qt(e, t) { + se(e.concat(t), function (e) { + const t = ie(e); + t.requestCount = (t.requestCount || 1) - 1; + }); + se(e, function (e) { + const t = ie(e); + if (t.requestCount === 0) { + e.classList.remove.call(e.classList, Q.config.requestClass); + } + }); + se(t, function (e) { + const t = ie(e); + if (t.requestCount === 0) { + e.removeAttribute("disabled"); + e.removeAttribute("data-disabled-by-htmx"); + } + }); + } + function en(t, n) { + for (let e = 0; e < t.length; e++) { + const r = t[e]; + if (r.isSameNode(n)) { + return true; + } + } + return false; + } + function tn(e) { + const t = e; + if ( + t.name === "" || + t.name == null || + t.disabled || + g(t, "fieldset[disabled]") + ) { + return false; + } + if ( + t.type === "button" || + t.type === "submit" || + t.tagName === "image" || + t.tagName === "reset" || + t.tagName === "file" + ) { + return false; + } + if (t.type === "checkbox" || t.type === "radio") { + return t.checked; + } + return true; + } + function nn(t, e, n) { + if (t != null && e != null) { + if (Array.isArray(e)) { + e.forEach(function (e) { + n.append(t, e); + }); + } else { + n.append(t, e); + } + } + } + function rn(t, n, r) { + if (t != null && n != null) { + let e = r.getAll(t); + if (Array.isArray(n)) { + e = e.filter((e) => n.indexOf(e) < 0); + } else { + e = e.filter((e) => e !== n); + } + r.delete(t); + se(e, (e) => r.append(t, e)); + } + } + function on(t, n, r, o, i) { + if (o == null || en(t, o)) { + return; + } else { + t.push(o); + } + if (tn(o)) { + const s = ee(o, "name"); + let e = o.value; + if (o instanceof HTMLSelectElement && o.multiple) { + e = M(o.querySelectorAll("option:checked")).map(function (e) { + return e.value; + }); + } + if (o instanceof HTMLInputElement && o.files) { + e = M(o.files); + } + nn(s, e, n); + if (i) { + sn(o, r); + } + } + if (o instanceof HTMLFormElement) { + se(o.elements, function (e) { + if (t.indexOf(e) >= 0) { + rn(e.name, e.value, n); + } else { + t.push(e); + } + if (i) { + sn(e, r); + } + }); + new FormData(o).forEach(function (e, t) { + if (e instanceof File && e.name === "") { + return; + } + nn(t, e, n); + }); + } + } + function sn(e, t) { + const n = e; + if (n.willValidate) { + de(n, "htmx:validation:validate"); + if (!n.checkValidity()) { + t.push({ elt: n, message: n.validationMessage, validity: n.validity }); + de(n, "htmx:validation:failed", { + message: n.validationMessage, + validity: n.validity, + }); + } + } + } + function ln(n, e) { + for (const t of e.keys()) { + n.delete(t); + } + e.forEach(function (e, t) { + n.append(t, e); + }); + return n; + } + function cn(e, t) { + const n = []; + const r = new FormData(); + const o = new FormData(); + const i = []; + const s = ie(e); + if (s.lastButtonClicked && !le(s.lastButtonClicked)) { + s.lastButtonClicked = null; + } + let l = + (e instanceof HTMLFormElement && e.noValidate !== true) || + te(e, "hx-validate") === "true"; + if (s.lastButtonClicked) { + l = l && s.lastButtonClicked.formNoValidate !== true; + } + if (t !== "get") { + on(n, o, i, g(e, "form"), l); + } + on(n, r, i, e, l); + if ( + s.lastButtonClicked || + e.tagName === "BUTTON" || + (e.tagName === "INPUT" && ee(e, "type") === "submit") + ) { + const u = s.lastButtonClicked || e; + const a = ee(u, "name"); + nn(a, u.value, o); + } + const c = we(e, "hx-include"); + se(c, function (e) { + on(n, r, i, ue(e), l); + if (!d(e, "form")) { + se(f(e).querySelectorAll(ot), function (e) { + on(n, r, i, e, l); + }); + } + }); + ln(r, o); + return { errors: i, formData: r, values: Nn(r) }; + } + function un(e, t, n) { + if (e !== "") { + e += "&"; + } + if (String(n) === "[object Object]") { + n = JSON.stringify(n); + } + const r = encodeURIComponent(n); + e += encodeURIComponent(t) + "=" + r; + return e; + } + function an(e) { + e = qn(e); + let n = ""; + e.forEach(function (e, t) { + n = un(n, t, e); + }); + return n; + } + function fn(e, t, n) { + const r = { + "HX-Request": "true", + "HX-Trigger": ee(e, "id"), + "HX-Trigger-Name": ee(e, "name"), + "HX-Target": te(t, "id"), + "HX-Current-URL": ne().location.href, + }; + bn(e, "hx-headers", false, r); + if (n !== undefined) { + r["HX-Prompt"] = n; + } + if (ie(e).boosted) { + r["HX-Boosted"] = "true"; + } + return r; + } + function dn(n, e) { + const t = re(e, "hx-params"); + if (t) { + if (t === "none") { + return new FormData(); + } else if (t === "*") { + return n; + } else if (t.indexOf("not ") === 0) { + se(t.substr(4).split(","), function (e) { + e = e.trim(); + n.delete(e); + }); + return n; + } else { + const r = new FormData(); + se(t.split(","), function (t) { + t = t.trim(); + if (n.has(t)) { + n.getAll(t).forEach(function (e) { + r.append(t, e); + }); + } + }); + return r; + } + } else { + return n; + } + } + function hn(e) { + return !!ee(e, "href") && ee(e, "href").indexOf("#") >= 0; + } + function gn(e, t) { + const n = t || re(e, "hx-swap"); + const r = { + swapStyle: ie(e).boosted ? "innerHTML" : Q.config.defaultSwapStyle, + swapDelay: Q.config.defaultSwapDelay, + settleDelay: Q.config.defaultSettleDelay, + }; + if (Q.config.scrollIntoViewOnBoost && ie(e).boosted && !hn(e)) { + r.show = "top"; + } + if (n) { + const s = F(n); + if (s.length > 0) { + for (let e = 0; e < s.length; e++) { + const l = s[e]; + if (l.indexOf("swap:") === 0) { + r.swapDelay = h(l.substr(5)); + } else if (l.indexOf("settle:") === 0) { + r.settleDelay = h(l.substr(7)); + } else if (l.indexOf("transition:") === 0) { + r.transition = l.substr(11) === "true"; + } else if (l.indexOf("ignoreTitle:") === 0) { + r.ignoreTitle = l.substr(12) === "true"; + } else if (l.indexOf("scroll:") === 0) { + const c = l.substr(7); + var o = c.split(":"); + const u = o.pop(); + var i = o.length > 0 ? o.join(":") : null; + r.scroll = u; + r.scrollTarget = i; + } else if (l.indexOf("show:") === 0) { + const a = l.substr(5); + var o = a.split(":"); + const f = o.pop(); + var i = o.length > 0 ? o.join(":") : null; + r.show = f; + r.showTarget = i; + } else if (l.indexOf("focus-scroll:") === 0) { + const d = l.substr("focus-scroll:".length); + r.focusScroll = d == "true"; + } else if (e == 0) { + r.swapStyle = l; + } else { + C("Unknown modifier in hx-swap: " + l); + } + } + } + } + return r; + } + function pn(e) { + return ( + re(e, "hx-encoding") === "multipart/form-data" || + (d(e, "form") && ee(e, "enctype") === "multipart/form-data") + ); + } + function mn(t, n, r) { + let o = null; + Ft(n, function (e) { + if (o == null) { + o = e.encodeParameters(t, r, n); + } + }); + if (o != null) { + return o; + } else { + if (pn(n)) { + return ln(new FormData(), qn(r)); + } else { + return an(r); + } + } + } + function xn(e) { + return { tasks: [], elts: [e] }; + } + function yn(e, t) { + const n = e[0]; + const r = e[e.length - 1]; + if (t.scroll) { + var o = null; + if (t.scrollTarget) { + o = ue(ae(n, t.scrollTarget)); + } + if (t.scroll === "top" && (n || o)) { + o = o || n; + o.scrollTop = 0; + } + if (t.scroll === "bottom" && (r || o)) { + o = o || r; + o.scrollTop = o.scrollHeight; + } + } + if (t.show) { + var o = null; + if (t.showTarget) { + let e = t.showTarget; + if (t.showTarget === "window") { + e = "body"; + } + o = ue(ae(n, e)); + } + if (t.show === "top" && (n || o)) { + o = o || n; + o.scrollIntoView({ block: "start", behavior: Q.config.scrollBehavior }); + } + if (t.show === "bottom" && (r || o)) { + o = o || r; + o.scrollIntoView({ block: "end", behavior: Q.config.scrollBehavior }); + } + } + } + function bn(r, e, o, i) { + if (i == null) { + i = {}; + } + if (r == null) { + return i; + } + const s = te(r, e); + if (s) { + let e = s.trim(); + let t = o; + if (e === "unset") { + return null; + } + if (e.indexOf("javascript:") === 0) { + e = e.substr(11); + t = true; + } else if (e.indexOf("js:") === 0) { + e = e.substr(3); + t = true; + } + if (e.indexOf("{") !== 0) { + e = "{" + e + "}"; + } + let n; + if (t) { + n = vn( + r, + function () { + return Function("return (" + e + ")")(); + }, + {}, + ); + } else { + n = S(e); + } + for (const l in n) { + if (n.hasOwnProperty(l)) { + if (i[l] == null) { + i[l] = n[l]; + } + } + } + } + return bn(ue(c(r)), e, o, i); + } + function vn(e, t, n) { + if (Q.config.allowEval) { + return t(); + } else { + fe(e, "htmx:evalDisallowedError"); + return n; + } + } + function wn(e, t) { + return bn(e, "hx-vars", true, t); + } + function Sn(e, t) { + return bn(e, "hx-vals", false, t); + } + function En(e) { + return ce(wn(e), Sn(e)); + } + function Cn(t, n, r) { + if (r !== null) { + try { + t.setRequestHeader(n, r); + } catch (e) { + t.setRequestHeader(n, encodeURIComponent(r)); + t.setRequestHeader(n + "-URI-AutoEncoded", "true"); + } + } + } + function On(t) { + if (t.responseURL && typeof URL !== "undefined") { + try { + const e = new URL(t.responseURL); + return e.pathname + e.search; + } catch (e) { + fe(ne().body, "htmx:badResponseUrl", { url: t.responseURL }); + } + } + } + function O(e, t) { + return t.test(e.getAllResponseHeaders()); + } + function Rn(t, n, r) { + t = t.toLowerCase(); + if (r) { + if (r instanceof Element || typeof r === "string") { + return he(t, n, null, null, { + targetOverride: y(r) || ve, + returnPromise: true, + }); + } else { + let e = y(r.target); + if ((r.target && !e) || (!e && !y(r.source))) { + e = ve; + } + return he(t, n, y(r.source), r.event, { + handler: r.handler, + headers: r.headers, + values: r.values, + targetOverride: e, + swapOverride: r.swap, + select: r.select, + returnPromise: true, + }); + } + } else { + return he(t, n, null, null, { returnPromise: true }); + } + } + function Hn(e) { + const t = []; + while (e) { + t.push(e); + e = e.parentElement; + } + return t; + } + function Tn(e, t, n) { + let r; + let o; + if (typeof URL === "function") { + o = new URL(t, document.location.href); + const i = document.location.origin; + r = i === o.origin; + } else { + o = t; + r = l(t, document.location.origin); + } + if (Q.config.selfRequestsOnly) { + if (!r) { + return false; + } + } + return de(e, "htmx:validateUrl", ce({ url: o, sameHost: r }, n)); + } + function qn(e) { + if (e instanceof FormData) return e; + const t = new FormData(); + for (const n in e) { + if (e.hasOwnProperty(n)) { + if (e[n] && typeof e[n].forEach === "function") { + e[n].forEach(function (e) { + t.append(n, e); + }); + } else if (typeof e[n] === "object" && !(e[n] instanceof Blob)) { + t.append(n, JSON.stringify(e[n])); + } else { + t.append(n, e[n]); + } + } + } + return t; + } + function Ln(r, o, e) { + return new Proxy(e, { + get: function (t, e) { + if (typeof e === "number") return t[e]; + if (e === "length") return t.length; + if (e === "push") { + return function (e) { + t.push(e); + r.append(o, e); + }; + } + if (typeof t[e] === "function") { + return function () { + t[e].apply(t, arguments); + r.delete(o); + t.forEach(function (e) { + r.append(o, e); + }); + }; + } + if (t[e] && t[e].length === 1) { + return t[e][0]; + } else { + return t[e]; + } + }, + set: function (e, t, n) { + e[t] = n; + r.delete(o); + e.forEach(function (e) { + r.append(o, e); + }); + return true; + }, + }); + } + function Nn(r) { + return new Proxy(r, { + get: function (e, t) { + if (typeof t === "symbol") { + return Reflect.get(e, t); + } + if (t === "toJSON") { + return () => Object.fromEntries(r); + } + if (t in e) { + if (typeof e[t] === "function") { + return function () { + return r[t].apply(r, arguments); + }; + } else { + return e[t]; + } + } + const n = r.getAll(t); + if (n.length === 0) { + return undefined; + } else if (n.length === 1) { + return n[0]; + } else { + return Ln(e, t, n); + } + }, + set: function (t, n, e) { + if (typeof n !== "string") { + return false; + } + t.delete(n); + if (e && typeof e.forEach === "function") { + e.forEach(function (e) { + t.append(n, e); + }); + } else if (typeof e === "object" && !(e instanceof Blob)) { + t.append(n, JSON.stringify(e)); + } else { + t.append(n, e); + } + return true; + }, + deleteProperty: function (e, t) { + if (typeof t === "string") { + e.delete(t); + } + return true; + }, + ownKeys: function (e) { + return Reflect.ownKeys(Object.fromEntries(e)); + }, + getOwnPropertyDescriptor: function (e, t) { + return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e), t); + }, + }); + } + function he(t, n, r, o, i, D) { + let s = null; + let l = null; + i = i != null ? i : {}; + if (i.returnPromise && typeof Promise !== "undefined") { + var e = new Promise(function (e, t) { + s = e; + l = t; + }); + } + if (r == null) { + r = ne().body; + } + const M = i.handler || Dn; + const X = i.select || null; + if (!le(r)) { + oe(s); + return e; + } + const c = i.targetOverride || ue(Ee(r)); + if (c == null || c == ve) { + fe(r, "htmx:targetError", { target: te(r, "hx-target") }); + oe(l); + return e; + } + let u = ie(r); + const a = u.lastButtonClicked; + if (a) { + const L = ee(a, "formaction"); + if (L != null) { + n = L; + } + const N = ee(a, "formmethod"); + if (N != null) { + if (N.toLowerCase() !== "dialog") { + t = N; + } + } + } + const f = re(r, "hx-confirm"); + if (D === undefined) { + const K = function (e) { + return he(t, n, r, o, i, !!e); + }; + const G = { + target: c, + elt: r, + path: n, + verb: t, + triggeringEvent: o, + etc: i, + issueRequest: K, + question: f, + }; + if (de(r, "htmx:confirm", G) === false) { + oe(s); + return e; + } + } + let d = r; + let h = re(r, "hx-sync"); + let g = null; + let F = false; + if (h) { + const A = h.split(":"); + const I = A[0].trim(); + if (I === "this") { + d = Se(r, "hx-sync"); + } else { + d = ue(ae(r, I)); + } + h = (A[1] || "drop").trim(); + u = ie(d); + if (h === "drop" && u.xhr && u.abortable !== true) { + oe(s); + return e; + } else if (h === "abort") { + if (u.xhr) { + oe(s); + return e; + } else { + F = true; + } + } else if (h === "replace") { + de(d, "htmx:abort"); + } else if (h.indexOf("queue") === 0) { + const W = h.split(" "); + g = (W[1] || "last").trim(); + } + } + if (u.xhr) { + if (u.abortable) { + de(d, "htmx:abort"); + } else { + if (g == null) { + if (o) { + const P = ie(o); + if (P && P.triggerSpec && P.triggerSpec.queue) { + g = P.triggerSpec.queue; + } + } + if (g == null) { + g = "last"; + } + } + if (u.queuedRequests == null) { + u.queuedRequests = []; + } + if (g === "first" && u.queuedRequests.length === 0) { + u.queuedRequests.push(function () { + he(t, n, r, o, i); + }); + } else if (g === "all") { + u.queuedRequests.push(function () { + he(t, n, r, o, i); + }); + } else if (g === "last") { + u.queuedRequests = []; + u.queuedRequests.push(function () { + he(t, n, r, o, i); + }); + } + oe(s); + return e; + } + } + const p = new XMLHttpRequest(); + u.xhr = p; + u.abortable = F; + const m = function () { + u.xhr = null; + u.abortable = false; + if (u.queuedRequests != null && u.queuedRequests.length > 0) { + const e = u.queuedRequests.shift(); + e(); + } + }; + const B = re(r, "hx-prompt"); + if (B) { + var x = prompt(B); + if (x === null || !de(r, "htmx:prompt", { prompt: x, target: c })) { + oe(s); + m(); + return e; + } + } + if (f && !D) { + if (!confirm(f)) { + oe(s); + m(); + return e; + } + } + let y = fn(r, c, x); + if (t !== "get" && !pn(r)) { + y["Content-Type"] = "application/x-www-form-urlencoded"; + } + if (i.headers) { + y = ce(y, i.headers); + } + const U = cn(r, t); + let b = U.errors; + const j = U.formData; + if (i.values) { + ln(j, qn(i.values)); + } + const V = qn(En(r)); + const v = ln(j, V); + let w = dn(v, r); + if (Q.config.getCacheBusterParam && t === "get") { + w.set("org.htmx.cache-buster", ee(c, "id") || "true"); + } + if (n == null || n === "") { + n = ne().location.href; + } + const S = bn(r, "hx-request"); + const _ = ie(r).boosted; + let E = Q.config.methodsThatUseUrlParams.indexOf(t) >= 0; + const C = { + boosted: _, + useUrlParams: E, + formData: w, + parameters: Nn(w), + unfilteredFormData: v, + unfilteredParameters: Nn(v), + headers: y, + target: c, + verb: t, + errors: b, + withCredentials: + i.credentials || S.credentials || Q.config.withCredentials, + timeout: i.timeout || S.timeout || Q.config.timeout, + path: n, + triggeringEvent: o, + }; + if (!de(r, "htmx:configRequest", C)) { + oe(s); + m(); + return e; + } + n = C.path; + t = C.verb; + y = C.headers; + w = qn(C.parameters); + b = C.errors; + E = C.useUrlParams; + if (b && b.length > 0) { + de(r, "htmx:validation:halted", C); + oe(s); + m(); + return e; + } + const z = n.split("#"); + const $ = z[0]; + const O = z[1]; + let R = n; + if (E) { + R = $; + const Z = !w.keys().next().done; + if (Z) { + if (R.indexOf("?") < 0) { + R += "?"; + } else { + R += "&"; + } + R += an(w); + if (O) { + R += "#" + O; + } + } + } + if (!Tn(r, R, C)) { + fe(r, "htmx:invalidPath", C); + oe(l); + return e; + } + p.open(t.toUpperCase(), R, true); + p.overrideMimeType("text/html"); + p.withCredentials = C.withCredentials; + p.timeout = C.timeout; + if (S.noHeaders) { + } else { + for (const k in y) { + if (y.hasOwnProperty(k)) { + const Y = y[k]; + Cn(p, k, Y); + } + } + } + const H = { + xhr: p, + target: c, + requestConfig: C, + etc: i, + boosted: _, + select: X, + pathInfo: { + requestPath: n, + finalRequestPath: R, + responsePath: null, + anchor: O, + }, + }; + p.onload = function () { + try { + const t = Hn(r); + H.pathInfo.responsePath = On(p); + M(r, H); + if (H.keepIndicators !== true) { + Qt(T, q); + } + de(r, "htmx:afterRequest", H); + de(r, "htmx:afterOnLoad", H); + if (!le(r)) { + let e = null; + while (t.length > 0 && e == null) { + const n = t.shift(); + if (le(n)) { + e = n; + } + } + if (e) { + de(e, "htmx:afterRequest", H); + de(e, "htmx:afterOnLoad", H); + } + } + oe(s); + m(); + } catch (e) { + fe(r, "htmx:onLoadError", ce({ error: e }, H)); + throw e; + } + }; + p.onerror = function () { + Qt(T, q); + fe(r, "htmx:afterRequest", H); + fe(r, "htmx:sendError", H); + oe(l); + m(); + }; + p.onabort = function () { + Qt(T, q); + fe(r, "htmx:afterRequest", H); + fe(r, "htmx:sendAbort", H); + oe(l); + m(); + }; + p.ontimeout = function () { + Qt(T, q); + fe(r, "htmx:afterRequest", H); + fe(r, "htmx:timeout", H); + oe(l); + m(); + }; + if (!de(r, "htmx:beforeRequest", H)) { + oe(s); + m(); + return e; + } + var T = Zt(r); + var q = Yt(r); + se(["loadstart", "loadend", "progress", "abort"], function (t) { + se([p, p.upload], function (e) { + e.addEventListener(t, function (e) { + de(r, "htmx:xhr:" + t, { + lengthComputable: e.lengthComputable, + loaded: e.loaded, + total: e.total, + }); + }); + }); + }); + de(r, "htmx:beforeSend", H); + const J = E ? null : mn(p, r, w); + p.send(J); + return e; + } + function An(e, t) { + const n = t.xhr; + let r = null; + let o = null; + if (O(n, /HX-Push:/i)) { + r = n.getResponseHeader("HX-Push"); + o = "push"; + } else if (O(n, /HX-Push-Url:/i)) { + r = n.getResponseHeader("HX-Push-Url"); + o = "push"; + } else if (O(n, /HX-Replace-Url:/i)) { + r = n.getResponseHeader("HX-Replace-Url"); + o = "replace"; + } + if (r) { + if (r === "false") { + return {}; + } else { + return { type: o, path: r }; + } + } + const i = t.pathInfo.finalRequestPath; + const s = t.pathInfo.responsePath; + const l = re(e, "hx-push-url"); + const c = re(e, "hx-replace-url"); + const u = ie(e).boosted; + let a = null; + let f = null; + if (l) { + a = "push"; + f = l; + } else if (c) { + a = "replace"; + f = c; + } else if (u) { + a = "push"; + f = s || i; + } + if (f) { + if (f === "false") { + return {}; + } + if (f === "true") { + f = s || i; + } + if (t.pathInfo.anchor && f.indexOf("#") === -1) { + f = f + "#" + t.pathInfo.anchor; + } + return { type: a, path: f }; + } else { + return {}; + } + } + function In(e, t) { + var n = new RegExp(e.code); + return n.test(t.toString(10)); + } + function Pn(e) { + for (var t = 0; t < Q.config.responseHandling.length; t++) { + var n = Q.config.responseHandling[t]; + if (In(n, e.status)) { + return n; + } + } + return { swap: false }; + } + function kn(e) { + if (e) { + const t = r("title"); + if (t) { + t.innerHTML = e; + } else { + window.document.title = e; + } + } + } + function Dn(o, i) { + const s = i.xhr; + let l = i.target; + const e = i.etc; + const c = i.select; + if (!de(o, "htmx:beforeOnLoad", i)) return; + if (O(s, /HX-Trigger:/i)) { + Je(s, "HX-Trigger", o); + } + if (O(s, /HX-Location:/i)) { + zt(); + let e = s.getResponseHeader("HX-Location"); + var t; + if (e.indexOf("{") === 0) { + t = S(e); + e = t.path; + delete t.path; + } + Rn("get", e, t).then(function () { + $t(e); + }); + return; + } + const n = + O(s, /HX-Refresh:/i) && s.getResponseHeader("HX-Refresh") === "true"; + if (O(s, /HX-Redirect:/i)) { + i.keepIndicators = true; + location.href = s.getResponseHeader("HX-Redirect"); + n && location.reload(); + return; + } + if (n) { + i.keepIndicators = true; + location.reload(); + return; + } + if (O(s, /HX-Retarget:/i)) { + if (s.getResponseHeader("HX-Retarget") === "this") { + i.target = o; + } else { + i.target = ue(ae(o, s.getResponseHeader("HX-Retarget"))); + } + } + const u = An(o, i); + const r = Pn(s); + const a = r.swap; + let f = !!r.error; + let d = Q.config.ignoreTitle || r.ignoreTitle; + let h = r.select; + if (r.target) { + i.target = ue(ae(o, r.target)); + } + var g = e.swapOverride; + if (g == null && r.swapOverride) { + g = r.swapOverride; + } + if (O(s, /HX-Retarget:/i)) { + if (s.getResponseHeader("HX-Retarget") === "this") { + i.target = o; + } else { + i.target = ue(ae(o, s.getResponseHeader("HX-Retarget"))); + } + } + if (O(s, /HX-Reswap:/i)) { + g = s.getResponseHeader("HX-Reswap"); + } + var p = s.response; + var m = ce( + { + shouldSwap: a, + serverResponse: p, + isError: f, + ignoreTitle: d, + selectOverride: h, + swapOverride: g, + }, + i, + ); + if (r.event && !de(l, r.event, m)) return; + if (!de(l, "htmx:beforeSwap", m)) return; + l = m.target; + p = m.serverResponse; + f = m.isError; + d = m.ignoreTitle; + h = m.selectOverride; + g = m.swapOverride; + i.target = l; + i.failed = f; + i.successful = !f; + if (m.shouldSwap) { + if (s.status === 286) { + lt(o); + } + Ft(o, function (e) { + p = e.transformResponse(p, s, o); + }); + if (u.type) { + zt(); + } + var x = gn(o, g); + if (!x.hasOwnProperty("ignoreTitle")) { + x.ignoreTitle = d; + } + l.classList.add(Q.config.swappingClass); + let n = null; + let r = null; + if (c) { + h = c; + } + if (O(s, /HX-Reselect:/i)) { + h = s.getResponseHeader("HX-Reselect"); + } + const y = re(o, "hx-select-oob"); + const b = re(o, "hx-select"); + let e = function () { + try { + if (u.type) { + de(ne().body, "htmx:beforeHistoryUpdate", ce({ history: u }, i)); + if (u.type === "push") { + $t(u.path); + de(ne().body, "htmx:pushedIntoHistory", { path: u.path }); + } else { + Jt(u.path); + de(ne().body, "htmx:replacedInHistory", { path: u.path }); + } + } + $e(l, p, x, { + select: h || b, + selectOOB: y, + eventInfo: i, + anchor: i.pathInfo.anchor, + contextElement: o, + afterSwapCallback: function () { + if (O(s, /HX-Trigger-After-Swap:/i)) { + let e = o; + if (!le(o)) { + e = ne().body; + } + Je(s, "HX-Trigger-After-Swap", e); + } + }, + afterSettleCallback: function () { + if (O(s, /HX-Trigger-After-Settle:/i)) { + let e = o; + if (!le(o)) { + e = ne().body; + } + Je(s, "HX-Trigger-After-Settle", e); + } + oe(n); + }, + }); + } catch (e) { + fe(o, "htmx:swapError", i); + oe(r); + throw e; + } + }; + let t = Q.config.globalViewTransitions; + if (x.hasOwnProperty("transition")) { + t = x.transition; + } + if ( + t && + de(o, "htmx:beforeTransition", i) && + typeof Promise !== "undefined" && + document.startViewTransition + ) { + const v = new Promise(function (e, t) { + n = e; + r = t; + }); + const w = e; + e = function () { + document.startViewTransition(function () { + w(); + return v; + }); + }; + } + if (x.swapDelay > 0) { + E().setTimeout(e, x.swapDelay); + } else { + e(); + } + } + if (f) { + fe( + o, + "htmx:responseError", + ce( + { + error: + "Response Status Error Code " + + s.status + + " from " + + i.pathInfo.requestPath, + }, + i, + ), + ); + } + } + const Mn = {}; + function Xn() { + return { + init: function (e) { + return null; + }, + getSelectors: function () { + return null; + }, + onEvent: function (e, t) { + return true; + }, + transformResponse: function (e, t, n) { + return e; + }, + isInlineSwap: function (e) { + return false; + }, + handleSwap: function (e, t, n, r) { + return false; + }, + encodeParameters: function (e, t, n) { + return null; + }, + }; + } + function Fn(e, t) { + if (t.init) { + t.init(n); + } + Mn[e] = ce(Xn(), t); + } + function Bn(e) { + delete Mn[e]; + } + function Un(e, n, r) { + if (n == undefined) { + n = []; + } + if (e == undefined) { + return n; + } + if (r == undefined) { + r = []; + } + const t = te(e, "hx-ext"); + if (t) { + se(t.split(","), function (e) { + e = e.replace(/ /g, ""); + if (e.slice(0, 7) == "ignore:") { + r.push(e.slice(7)); + return; + } + if (r.indexOf(e) < 0) { + const t = Mn[e]; + if (t && n.indexOf(t) < 0) { + n.push(t); + } + } + }); + } + return Un(ue(c(e)), n, r); + } + var jn = false; + ne().addEventListener("DOMContentLoaded", function () { + jn = true; + }); + function Vn(e) { + if (jn || ne().readyState === "complete") { + e(); + } else { + ne().addEventListener("DOMContentLoaded", e); + } + } + function _n() { + if (Q.config.includeIndicatorStyles !== false) { + const e = Q.config.inlineStyleNonce + ? ` nonce="${Q.config.inlineStyleNonce}"` + : ""; + ne().head.insertAdjacentHTML( + "beforeend", + " ." + + Q.config.indicatorClass + + "{opacity:0} ." + + Q.config.requestClass + + " ." + + Q.config.indicatorClass + + "{opacity:1; transition: opacity 200ms ease-in;} ." + + Q.config.requestClass + + "." + + Q.config.indicatorClass + + "{opacity:1; transition: opacity 200ms ease-in;} ", + ); + } + } + function zn() { + const e = ne().querySelector('meta[name="htmx-config"]'); + if (e) { + return S(e.content); + } else { + return null; + } + } + function $n() { + const e = zn(); + if (e) { + Q.config = ce(Q.config, e); + } + } + Vn(function () { + $n(); + _n(); + let e = ne().body; + kt(e); + const t = ne().querySelectorAll( + "[hx-trigger='restored'],[data-hx-trigger='restored']", + ); + e.addEventListener("htmx:abort", function (e) { + const t = e.target; + const n = ie(t); + if (n && n.xhr) { + n.xhr.abort(); + } + }); + const n = window.onpopstate ? window.onpopstate.bind(window) : null; + window.onpopstate = function (e) { + if (e.state && e.state.htmx) { + Wt(); + se(t, function (e) { + de(e, "htmx:restored", { document: ne(), triggerEvent: de }); + }); + } else { + if (n) { + n(e); + } + } + }; + E().setTimeout(function () { + de(e, "htmx:load", {}); + e = null; + }, 0); + }); + return Q; +})(); diff --git a/cmd/web/base.templ b/cmd/web/base.templ new file mode 100644 index 0000000..7484dba --- /dev/null +++ b/cmd/web/base.templ @@ -0,0 +1,18 @@ +package web + +templ Base() { + + + + + Go Blueprint Hello + + + + +
+ { children... } +
+ + +} diff --git a/cmd/web/efs.go b/cmd/web/efs.go new file mode 100644 index 0000000..57b9761 --- /dev/null +++ b/cmd/web/efs.go @@ -0,0 +1,6 @@ +package web + +import "embed" + +//go:embed "assets" +var Files embed.FS diff --git a/cmd/web/hello.go b/cmd/web/hello.go new file mode 100644 index 0000000..98cd24e --- /dev/null +++ b/cmd/web/hello.go @@ -0,0 +1,21 @@ +package web + +import ( + "log" + "net/http" +) + +func HelloWebHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + http.Error(w, "Bad Request", http.StatusBadRequest) + } + + name := r.FormValue("name") + component := HelloPost(name) + err = component.Render(r.Context(), w) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + log.Fatalf("Error rendering in HelloWebHandler: %e", err) + } +} diff --git a/cmd/web/hello.templ b/cmd/web/hello.templ new file mode 100644 index 0000000..f5f5d72 --- /dev/null +++ b/cmd/web/hello.templ @@ -0,0 +1,17 @@ +package web + +templ HelloForm() { + @Base() { +
+ + +
+
+ } +} + +templ HelloPost(name string) { +
+

Hello, { name }

+
+} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3873163 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,44 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + target: prod + restart: unless-stopped + ports: + - ${PORT}:${PORT} + environment: + APP_ENV: ${APP_ENV} + PORT: ${PORT} + BLUEPRINT_DB_HOST: ${BLUEPRINT_DB_HOST} + BLUEPRINT_DB_PORT: ${BLUEPRINT_DB_PORT} + BLUEPRINT_DB_USERNAME: ${BLUEPRINT_DB_USERNAME} + BLUEPRINT_DB_ROOT_PASSWORD: ${BLUEPRINT_DB_ROOT_PASSWORD} + depends_on: + mongo_bp: + condition: service_healthy + networks: + - blueprint + mongo_bp: + image: mongo:latest + restart: unless-stopped + environment: + MONGO_INITDB_ROOT_USERNAME: ${BLUEPRINT_DB_USERNAME} + MONGO_INITDB_ROOT_PASSWORD: ${BLUEPRINT_DB_ROOT_PASSWORD} + ports: + - "${BLUEPRINT_DB_PORT}:27017" + volumes: + - mongo_volume_bp:/data/db + healthcheck: + test: ["CMD","mongosh", "--eval", "db.adminCommand('ping')"] + interval: 5s + timeout: 5s + retries: 3 + start_period: 15s + networks: + - blueprint + +volumes: + mongo_volume_bp: +networks: + blueprint: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9bfd962 --- /dev/null +++ b/go.mod @@ -0,0 +1,92 @@ +module blueprint_fix + +go 1.23.1 + +require ( + github.com/a-h/templ v0.3.833 + github.com/coder/websocket v1.8.12 + github.com/gin-gonic/gin v1.10.0 + github.com/joho/godotenv v1.5.1 + github.com/testcontainers/testcontainers-go/modules/mongodb v0.35.0 + go.mongodb.org/mongo-driver v1.17.3 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/bytedance/sonic v1.12.10 // indirect + github.com/bytedance/sonic/loader v0.2.3 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.0.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.25.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/stretchr/testify v1.10.0 // indirect + github.com/testcontainers/testcontainers-go v0.35.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/arch v0.15.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ffe0e41 --- /dev/null +++ b/go.sum @@ -0,0 +1,283 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU= +github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk= +github.com/bytedance/sonic v1.12.10 h1:uVCQr6oS5669E9ZVW0HyksTLfNS7Q/9hV6IVS4nEMsI= +github.com/bytedance/sonic v1.12.10/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0= +github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= +github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= +github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= +github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= +github.com/testcontainers/testcontainers-go/modules/mongodb v0.35.0 h1:i1Kh9fmXgHG9z3uzJv5Arz7pDKVaaNpLrqyd+0xhYMA= +github.com/testcontainers/testcontainers-go/modules/mongodb v0.35.0/go.mod h1:SD8nVMK1m7b/K2YJqYjYNzfHmZfqHtqNOlI44nfxjdg= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= +go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= +golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/internal/database/database.go b/internal/database/database.go new file mode 100644 index 0000000..d74ea65 --- /dev/null +++ b/internal/database/database.go @@ -0,0 +1,53 @@ +package database + +import ( + "context" + "fmt" + "log" + "os" + "time" + + _ "github.com/joho/godotenv/autoload" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type Service interface { + Health() map[string]string +} + +type service struct { + db *mongo.Client +} + +var ( + host = os.Getenv("BLUEPRINT_DB_HOST") + port = os.Getenv("BLUEPRINT_DB_PORT") + //database = os.Getenv("BLUEPRINT_DB_DATABASE") +) + +func New() Service { + client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%s", host, port))) + + if err != nil { + log.Fatal(err) + + } + return &service{ + db: client, + } +} + +func (s *service) Health() map[string]string { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + err := s.db.Ping(ctx, nil) + if err != nil { + log.Fatalf(fmt.Sprintf("db down: %v", err)) + } + + return map[string]string{ + "message": "It's healthy", + } +} diff --git a/internal/database/database_test.go b/internal/database/database_test.go new file mode 100644 index 0000000..014e9c8 --- /dev/null +++ b/internal/database/database_test.go @@ -0,0 +1,61 @@ +package database + +import ( + "context" + "log" + "testing" + + "github.com/testcontainers/testcontainers-go/modules/mongodb" +) + +func mustStartMongoContainer() (func(context.Context) error, error) { + dbContainer, err := mongodb.Run(context.Background(), "mongo:latest") + if err != nil { + return nil, err + } + + dbHost, err := dbContainer.Host(context.Background()) + if err != nil { + return dbContainer.Terminate, err + } + + dbPort, err := dbContainer.MappedPort(context.Background(), "27017/tcp") + if err != nil { + return dbContainer.Terminate, err + } + + host = dbHost + port = dbPort.Port() + + return dbContainer.Terminate, err +} + +func TestMain(m *testing.M) { + teardown, err := mustStartMongoContainer() + if err != nil { + log.Fatalf("could not start mongodb container: %v", err) + } + + m.Run() + + if teardown != nil && teardown(context.Background()) != nil { + log.Fatalf("could not teardown mongodb container: %v", err) + } +} + +func TestNew(t *testing.T) { + srv := New() + if srv == nil { + t.Fatal("New() returned nil") + } +} + +func TestHealth(t *testing.T) { + srv := New() + + stats := srv.Health() + + if stats["message"] != "It's healthy" { + t.Fatalf("expected message to be 'It's healthy', got %s", stats["message"]) + } +} diff --git a/internal/server/routes.go b/internal/server/routes.go new file mode 100644 index 0000000..93d36e4 --- /dev/null +++ b/internal/server/routes.go @@ -0,0 +1,78 @@ +package server + +import ( + "net/http" + + "fmt" + "log" + "time" + + "github.com/gin-gonic/gin" + + "blueprint_fix/cmd/web" + "github.com/a-h/templ" + "io/fs" + + "github.com/coder/websocket" +) + +func (s *Server) RegisterRoutes() http.Handler { + r := gin.Default() + + r.GET("/", s.HelloWorldHandler) + + r.GET("/health", s.healthHandler) + + r.GET("/websocket", s.websocketHandler) + + staticFiles, _ := fs.Sub(web.Files, "assets") + r.StaticFS("/assets", http.FS(staticFiles)) + + r.GET("/web", func(c *gin.Context) { + templ.Handler(web.HelloForm()).ServeHTTP(c.Writer, c.Request) + }) + + r.POST("/hello", func(c *gin.Context) { + web.HelloWebHandler(c.Writer, c.Request) + }) + + return r +} + +func (s *Server) HelloWorldHandler(c *gin.Context) { + resp := make(map[string]string) + resp["message"] = "Hello World" + + c.JSON(http.StatusOK, resp) +} + +func (s *Server) healthHandler(c *gin.Context) { + c.JSON(http.StatusOK, s.db.Health()) +} + +func (s *Server) websocketHandler(c *gin.Context) { + w := c.Writer + r := c.Request + socket, err := websocket.Accept(w, r, nil) + + if err != nil { + log.Printf("could not open websocket: %v", err) + _, _ = w.Write([]byte("could not open websocket")) + w.WriteHeader(http.StatusInternalServerError) + return + } + + defer socket.Close(websocket.StatusGoingAway, "server closing websocket") + + ctx := r.Context() + socketCtx := socket.CloseRead(ctx) + + for { + payload := fmt.Sprintf("server timestamp: %d", time.Now().UnixNano()) + err := socket.Write(socketCtx, websocket.MessageText, []byte(payload)) + if err != nil { + break + } + time.Sleep(time.Second * 2) + } +} diff --git a/internal/server/routes_test.go b/internal/server/routes_test.go new file mode 100644 index 0000000..db2233e --- /dev/null +++ b/internal/server/routes_test.go @@ -0,0 +1,32 @@ +package server + +import ( + "github.com/gin-gonic/gin" + "net/http" + "net/http/httptest" + "testing" +) + +func TestHelloWorldHandler(t *testing.T) { + s := &Server{} + r := gin.New() + r.GET("/", s.HelloWorldHandler) + // Create a test HTTP request + req, err := http.NewRequest("GET", "/", nil) + if err != nil { + t.Fatal(err) + } + // Create a ResponseRecorder to record the response + rr := httptest.NewRecorder() + // Serve the HTTP request + r.ServeHTTP(rr, req) + // Check the status code + if status := rr.Code; status != http.StatusOK { + t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + // Check the response body + expected := "{\"message\":\"Hello World\"}" + if rr.Body.String() != expected { + t.Errorf("Handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} diff --git a/internal/server/server.go b/internal/server/server.go new file mode 100644 index 0000000..67844c8 --- /dev/null +++ b/internal/server/server.go @@ -0,0 +1,39 @@ +package server + +import ( + "fmt" + "net/http" + "os" + "strconv" + "time" + + _ "github.com/joho/godotenv/autoload" + + "blueprint_fix/internal/database" +) + +type Server struct { + port int + + db database.Service +} + +func NewServer() *http.Server { + port, _ := strconv.Atoi(os.Getenv("PORT")) + NewServer := &Server{ + port: port, + + db: database.New(), + } + + // Declare Server config + server := &http.Server{ + Addr: fmt.Sprintf(":%d", NewServer.port), + Handler: NewServer.RegisterRoutes(), + IdleTimeout: time.Minute, + ReadTimeout: 10 * time.Second, + WriteTimeout: 30 * time.Second, + } + + return server +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..4ec3451 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./cmd/web/**/*.html", "./cmd/web/**/*.templ", + ], + theme: { + extend: {}, + }, + plugins: [], +} +