aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-08-21 14:10:15 -0400
committerXe Iaso <me@xeiaso.net>2024-08-21 14:20:44 -0400
commit39660d978304ebce08a9200f4ef63c0013cab7ac (patch)
tree68f1b7f77e1690d5e7021cb221f454dff146905f /cmd
parentde3aa62dbc608ec1fbcf676275340644ed1fd031 (diff)
downloadx-39660d978304ebce08a9200f4ef63c0013cab7ac.tar.xz
x-39660d978304ebce08a9200f4ef63c0013cab7ac.zip
cmd/hdrwtch: closer to implementation
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/hdrwtch/dao.go42
-rw-r--r--cmd/hdrwtch/execute.go47
-rw-r--r--cmd/hdrwtch/main.go45
-rw-r--r--cmd/hdrwtch/probes.go61
-rw-r--r--cmd/hdrwtch/probes.templ120
-rw-r--r--cmd/hdrwtch/probes_templ.go251
-rw-r--r--cmd/hdrwtch/static/css/styles.css3
-rw-r--r--cmd/hdrwtch/static/img/logo.svg9
-rw-r--r--cmd/hdrwtch/styles.css6
-rw-r--r--cmd/hdrwtch/telegram.go6
-rw-r--r--cmd/hdrwtch/web.templ23
-rw-r--r--cmd/hdrwtch/web_templ.go75
-rw-r--r--cmd/xedn/main.go2
13 files changed, 490 insertions, 200 deletions
diff --git a/cmd/hdrwtch/dao.go b/cmd/hdrwtch/dao.go
index 8693cf6..a651e21 100644
--- a/cmd/hdrwtch/dao.go
+++ b/cmd/hdrwtch/dao.go
@@ -9,6 +9,7 @@ import (
"github.com/ncruces/go-sqlite3/gormlite"
slogGorm "github.com/orandin/slog-gorm"
"gorm.io/gorm"
+ "gorm.io/gorm/clause"
gormPrometheus "gorm.io/plugin/prometheus"
)
@@ -46,6 +47,47 @@ func New(dbLoc string) (*DAO, error) {
return &DAO{db: db}, nil
}
+func (dao *DAO) UpsertUser(ctx context.Context, user *TelegramUser) error {
+ if err := dao.db.Clauses(clause.OnConflict{
+ Columns: []clause.Column{{Name: "id"}}, // primary key
+ DoUpdates: clause.AssignmentColumns([]string{"first_name", "last_name", "photo_url", "auth_date"}), // column needed to be updated
+ }).
+ Create(user).
+ WithContext(ctx).
+ Error; err != nil {
+ return fmt.Errorf("failed to create user: %w", err)
+ }
+
+ return nil
+}
+
+func (dao *DAO) CreateProbe(ctx context.Context, probe *Probe, tu *TelegramUser) error {
+ // Check if user has reached probe limit
+ var count int64
+ if err := dao.db.Model(&Probe{}).Where("user_id = ?", tu.ID).Count(&count).Error; err != nil {
+ return fmt.Errorf("failed to count probes: %w", err)
+ }
+
+ if count >= int64(tu.ProbeLimit) {
+ return fmt.Errorf("probe limit reached")
+ }
+
+ if err := dao.db.Create(probe).WithContext(ctx).Error; err != nil {
+ return fmt.Errorf("failed to create probe: %w", err)
+ }
+
+ return nil
+}
+
+func (dao *DAO) CountProbes(ctx context.Context, userID string) (int64, error) {
+ var count int64
+ if err := dao.db.Model(&Probe{}).Where("user_id = ?", userID).Count(&count).WithContext(ctx).Error; err != nil {
+ return 0, fmt.Errorf("failed to count probes: %w", err)
+ }
+
+ return count, nil
+}
+
func (dao *DAO) GetProbe(ctx context.Context, id string, userID string) (*Probe, error) {
var probe Probe
diff --git a/cmd/hdrwtch/execute.go b/cmd/hdrwtch/execute.go
new file mode 100644
index 0000000..9cefd51
--- /dev/null
+++ b/cmd/hdrwtch/execute.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "within.website/x/web/useragent"
+)
+
+func checkURL(ctx context.Context, probe Probe) (*ProbeResult, error) {
+ result := &ProbeResult{
+ ProbeID: probe.ID,
+ Region: *region,
+ }
+
+ userAgent := useragent.GenUserAgent("hdrwtch/1.0", fmt.Sprintf("https://%s/docs/why-in-logs", *domain))
+
+ start := time.Now()
+ req, err := http.NewRequestWithContext(ctx, "GET", probe.URL, nil)
+ if err != nil {
+ result.Success = false
+ result.Remark = fmt.Sprintf("failed to create request: %v", err)
+ return result, fmt.Errorf("failed to create request: %w", err)
+ }
+
+ req.Header.Set("User-Agent", userAgent)
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ result.Success = false
+ result.Remark = fmt.Sprintf("failed to execute request: %v", err)
+ return result, fmt.Errorf("failed to execute request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ result.Success = true
+ result.StatusCode = resp.StatusCode
+ result.Duration = time.Since(start)
+
+ return &ProbeResult{
+ ProbeID: probe.ID,
+ StatusCode: resp.StatusCode,
+ Duration: time.Since(start),
+ }, nil
+}
diff --git a/cmd/hdrwtch/main.go b/cmd/hdrwtch/main.go
index 9ee4bc9..b982725 100644
--- a/cmd/hdrwtch/main.go
+++ b/cmd/hdrwtch/main.go
@@ -8,11 +8,11 @@ import (
"log/slog"
"net/http"
"os"
+ "time"
"github.com/a-h/templ"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
- "gorm.io/gorm/clause"
"within.website/x/htmx"
"within.website/x/internal"
)
@@ -26,7 +26,9 @@ var (
cookieSecret = flag.String("cookie-secret", "", "Secret key for cookie store")
dbURL = flag.String("database-url", "", "Database URL")
dbLoc = flag.String("database-loc", "./var/hdrwtch.db", "Database location")
+ domain = flag.String("domain", "shiroko-wsl.shark-harmonic.ts.net", "Domain to use for user agent")
port = flag.String("port", "8080", "Port to listen on")
+ region = flag.String("fly-region", "yow-dev", "Region of this instance")
//go:embed static
staticFS embed.FS
@@ -55,7 +57,7 @@ func main() {
htmx.Mount(mux)
- mux.Handle("/static/", http.FileServer(http.FS(staticFS)))
+ mux.Handle("/static/", internal.UnchangingCache(http.FileServer(http.FS(staticFS))))
mux.Handle("/{$}", templ.Handler(base("Home", nil, anonNavBar(true), homePage())))
mux.HandleFunc("/login", s.loginHandler)
@@ -73,11 +75,25 @@ func main() {
mux.Handle("PUT /probe/{id}", s.loggedIn(s.probeUpdate))
mux.Handle("DELETE /probe/{id}", s.loggedIn(s.probeDelete))
- mux.Handle("/", templ.Handler(
- base("Not Found", nil, anonNavBar(true), notFoundPage()),
- templ.WithStatus(http.StatusNotFound),
+ mux.Handle("/", internal.UnchangingCache(
+ templ.Handler(
+ base("Not Found", nil, anonNavBar(true), notFoundPage()),
+ templ.WithStatus(http.StatusNotFound),
+ ),
))
+ // test routes
+ mux.HandleFunc("GET /test/curr", func(w http.ResponseWriter, r *http.Request) {
+ val := time.Now().Format(http.TimeFormat)
+ w.Header().Set("Last-Modified", val)
+ fmt.Fprintln(w, val)
+ })
+ mux.HandleFunc("GET /test/constant", func(w http.ResponseWriter, r *http.Request) {
+ val := "Mon, 02 Jan 2006 15:04:05 GMT"
+ w.Header().Set("Last-Modified", val)
+ fmt.Fprintln(w, val)
+ })
+
slog.Info("listening", "on", "http://localhost:"+*port)
log.Fatal(http.ListenAndServe(":"+*port, mux))
@@ -126,11 +142,7 @@ func (s *Server) loginCallbackHandler(w http.ResponseWriter, r *http.Request) {
return
}
- if err := s.dao.db.Clauses(clause.OnConflict{
- Columns: []clause.Column{{Name: "id"}}, // primary key
- DoUpdates: clause.AssignmentColumns([]string{"first_name", "last_name", "photo_url", "auth_date"}), // column needed to be updated
- }).Create(user).WithContext(r.Context()).Error; err != nil {
- slog.Error("failed to create user", "err", err, "user", user)
+ if err := s.dao.UpsertUser(r.Context(), user); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -139,9 +151,16 @@ func (s *Server) loginCallbackHandler(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) userHandler(w http.ResponseWriter, r *http.Request) {
- userData, ok := r.Context().Value(ctxKeyTelegramUser).(*TelegramUser)
+ userData, ok := s.getTelegramUserData(r)
if !ok {
- http.Error(w, "no user data", http.StatusUnauthorized)
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
+ return
+ }
+
+ probeCount, err := s.dao.CountProbes(r.Context(), userData.ID)
+ if err != nil {
+ slog.Error("failed to count probes", "err", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -150,7 +169,7 @@ func (s *Server) userHandler(w http.ResponseWriter, r *http.Request) {
"User Info",
nil,
authedNavBar(userData),
- userPage(userData),
+ userPage(userData, probeCount),
),
).ServeHTTP(w, r)
}
diff --git a/cmd/hdrwtch/probes.go b/cmd/hdrwtch/probes.go
index b9a8c3a..255ac4d 100644
--- a/cmd/hdrwtch/probes.go
+++ b/cmd/hdrwtch/probes.go
@@ -3,6 +3,7 @@ package main
import (
"log/slog"
"net/http"
+ "time"
"github.com/a-h/templ"
"gorm.io/gorm"
@@ -14,16 +15,19 @@ type Probe struct {
UserID string
Name string
URL string
- LastResultID int
+ LastResultID uint
LastResult ProbeResult
}
type ProbeResult struct {
gorm.Model
- ProbeID int
+ ProbeID uint
+ Success bool
LastModified string
StatusCode int
Region string
+ Remark string
+ Duration time.Duration
}
func (s *Server) probeList(w http.ResponseWriter, r *http.Request) {
@@ -90,17 +94,13 @@ func (s *Server) probeEdit(w http.ResponseWriter, r *http.Request) {
return
}
- probe, ok := getProbe(r.Context())
- if !ok {
+ probe, err := s.dao.GetProbe(r.Context(), r.PathValue("id"), tu.ID)
+ if err != nil {
+ slog.Error("failed to get probe", "path", r.URL.Path, "err", err)
http.Error(w, "no probe data", http.StatusUnauthorized)
return
}
- if probe.UserID != tu.ID {
- http.Error(w, "unauthorized", http.StatusUnauthorized)
- return
- }
-
templ.Handler(
probeEdit(*probe),
).ServeHTTP(w, r)
@@ -114,18 +114,12 @@ func (s *Server) probeGet(w http.ResponseWriter, r *http.Request) {
}
probe, err := s.dao.GetProbe(r.Context(), r.PathValue("id"), tu.ID)
- slog.Info("probe", "probe", probe)
if err != nil {
slog.Error("failed to get probe", "path", r.URL.Path, "err", err)
http.Error(w, "no probe data", http.StatusUnauthorized)
return
}
- if probe.UserID != tu.ID {
- http.Error(w, "unauthorized", http.StatusUnauthorized)
- return
- }
-
if htmx.Is(r) {
templ.Handler(
probeRow(*probe),
@@ -140,7 +134,7 @@ func (s *Server) probeGet(w http.ResponseWriter, r *http.Request) {
}
templ.Handler(
- base("Probe "+probe.Name, nil, authedNavBar(tu), probePage(*probe, results)),
+ base(probe.Name, nil, authedNavBar(tu), probePage(*probe, results)),
).ServeHTTP(w, r)
}
}
@@ -152,16 +146,31 @@ func (s *Server) probeUpdate(w http.ResponseWriter, r *http.Request) {
return
}
- probe, ok := getProbe(r.Context())
- if !ok {
+ probe, err := s.dao.GetProbe(r.Context(), r.PathValue("id"), tu.ID)
+ if err != nil {
+ slog.Error("failed to get probe", "path", r.URL.Path, "err", err)
http.Error(w, "no probe data", http.StatusUnauthorized)
return
}
- if probe.UserID != tu.ID {
- http.Error(w, "unauthorized", http.StatusUnauthorized)
+ if err := r.ParseForm(); err != nil {
+ slog.Error("failed to parse form", "err", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ probe.Name = r.FormValue("name")
+ probe.URL = r.FormValue("url")
+
+ if err := s.dao.db.Save(probe).Error; err != nil {
+ slog.Error("failed to update probe", "err", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
+
+ templ.Handler(
+ probeRow(*probe),
+ ).ServeHTTP(w, r)
}
func (s *Server) probeDelete(w http.ResponseWriter, r *http.Request) {
@@ -171,14 +180,18 @@ func (s *Server) probeDelete(w http.ResponseWriter, r *http.Request) {
return
}
- probe, ok := getProbe(r.Context())
- if !ok {
+ probe, err := s.dao.GetProbe(r.Context(), r.PathValue("id"), tu.ID)
+ if err != nil {
+ slog.Error("failed to get probe", "path", r.URL.Path, "err", err)
http.Error(w, "no probe data", http.StatusUnauthorized)
return
}
- if probe.UserID != tu.ID {
- http.Error(w, "unauthorized", http.StatusUnauthorized)
+ if err := s.dao.db.Delete(probe).Error; err != nil {
+ slog.Error("failed to delete probe", "err", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
+
+ http.Redirect(w, r, "/probes", http.StatusFound)
}
diff --git a/cmd/hdrwtch/probes.templ b/cmd/hdrwtch/probes.templ
index b21dc3c..8d4bf8d 100644
--- a/cmd/hdrwtch/probes.templ
+++ b/cmd/hdrwtch/probes.templ
@@ -7,21 +7,43 @@ import (
templ probeListPage(probes []Probe) {
<div id="parent">
- <table class="table-auto p-4" id="table">
- <thead>
- <tr>
- <th class="py-2 px-6">Name</th>
- <th class="py-2 px-6">URL</th>
- <th class="py-2 px-6">Last Result</th>
- <th class="py-2 px-6"></th>
- </tr>
- </thead>
- <tbody hx-target="closest tr" hx-swap="outerHTML">
- for _, probe := range probes {
- @probeRow(probe)
- }
- </tbody>
- </table>
+ <div class="mt-8 flow-root">
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
+ <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
+ <table class="min-w-full divide-y divide-gray-300">
+ <thead>
+ <tr>
+ <th
+ scope="col"
+ class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0"
+ >Name</th>
+ <th
+ scope="col"
+ class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >URL</th>
+ <th
+ scope="col"
+ class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >Last Result</th>
+ <th
+ scope="col"
+ class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
+ ></th>
+ </tr>
+ </thead>
+ <tbody
+ class="divide-y divide-gray-200"
+ hx-target="closest tr"
+ hx-swap="outerHTML"
+ >
+ for _, probe := range probes {
+ @probeRow(probe)
+ }
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
@probeCreateForm()
</div>
}
@@ -77,9 +99,13 @@ templ probeCreateForm() {
templ probeRow(probe Probe) {
<tr>
- <td class="">{ probe.Name }</td>
- <td class="">{ probe.URL }</td>
- <td class=""><a href={ templ.SafeURL(fmt.Sprintf("/probe/%d/run/%d", probe.ID, probe.LastResult.ID)) }>{ probe.LastResult.LastModified }</a></td>
+ <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0"><a href={ templ.SafeURL(fmt.Sprintf("/probe/%d", probe.ID)) }>{ probe.Name }</a></td>
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"><code>{ probe.URL }</code></td>
+ if probe.LastResult.CreatedAt.IsZero() {
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">Not run yet</td>
+ } else {
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"><a href={ templ.SafeURL(fmt.Sprintf("/probe/%d/run/%d", probe.ID, probe.LastResult.ID)) }>{ probe.LastResult.CreatedAt.Format(time.RFC3339) }</a></td>
+ }
<td>
<button
class="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2"
@@ -221,38 +247,38 @@ templ probePage(probe Probe, history []ProbeResult) {
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h2 class="text-base font-semibold leading-6 text-gray-900">Run history</h2>
- <p class="mt-2 text-sm text-gray-700">The top 15 runs of this probe.</p>
+ <p class="mt-2 text-sm text-gray-700">The most recent 15 runs of this probe.</p>
</div>
</div>
- <div class="mt-8 flow-root">
- <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
- <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
- <table class="min-w-full divide-y divide-gray-300">
- <thead>
- <tr>
- <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">Name</th>
- <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Title</th>
- <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Email</th>
- <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Role</th>
- <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
- <span class="sr-only">Edit</span>
- </th>
- </tr>
- </thead>
- <tbody class="divide-y divide-gray-200">
- <tr>
- <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">Lindsay Walton</td>
- <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">Front-end Developer</td>
- <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">lindsay.walton@example.com</td>
- <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">Member</td>
- <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
- <a href="#" class="text-indigo-600 hover:text-indigo-900">Edit<span class="sr-only">, Lindsay Walton</span></a>
- </td>
- </tr>
- </tbody>
- </table>
+ if len(history) != 0 {
+ <div class="mt-8 flow-root">
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
+ <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
+ <table class="min-w-full divide-y divide-gray-300">
+ <thead>
+ <tr>
+ <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">Time</th>
+ <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Status code</th>
+ <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Region</th>
+ <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Last Modified</th>
+ </tr>
+ </thead>
+ <tbody class="divide-y divide-gray-200">
+ for _, check := range history {
+ <tr>
+ <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">{ check.CreatedAt.Format(time.RFC3339) }</td>
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{ fmt.Sprint(check.StatusCode) }</td>
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{ check.Region }</td>
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{ check.LastModified }</td>
+ </tr>
+ }
+ </tbody>
+ </table>
+ </div>
</div>
</div>
- </div>
+ } else {
+ <p class="mt-8 flow-root">This probe has not been run yet, wait 15 minutes or so for it to be run.</p>
+ }
</div>
}
diff --git a/cmd/hdrwtch/probes_templ.go b/cmd/hdrwtch/probes_templ.go
index ee3c877..6f1f659 100644
--- a/cmd/hdrwtch/probes_templ.go
+++ b/cmd/hdrwtch/probes_templ.go
@@ -31,7 +31,7 @@ func probeListPage(probes []Probe) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div id=\"parent\"><table class=\"table-auto p-4\" id=\"table\"><thead><tr><th class=\"py-2 px-6\">Name</th><th class=\"py-2 px-6\">URL</th><th class=\"py-2 px-6\">Last Result</th><th class=\"py-2 px-6\"></th></tr></thead> <tbody hx-target=\"closest tr\" hx-swap=\"outerHTML\">")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div id=\"parent\"><div class=\"mt-8 flow-root\"><div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\"><div class=\"inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8\"><table class=\"min-w-full divide-y divide-gray-300\"><thead><tr><th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0\">Name</th><th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">URL</th><th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Last Result</th><th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\"></th></tr></thead> <tbody class=\"divide-y divide-gray-200\" hx-target=\"closest tr\" hx-swap=\"outerHTML\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -41,7 +41,7 @@ func probeListPage(probes []Probe) templ.Component {
return templ_7745c5c3_Err
}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table>")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -101,64 +101,88 @@ func probeRow(probe Probe) templ.Component {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td class=\"\">")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(probe.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 80, Col: 27}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
+ var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/probe/%d", probe.ID))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"\">")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(probe.URL)
+ templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(probe.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 81, Col: 26}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 102, Col: 163}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td class=\"\"><a href=\"")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></td><td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500\"><code>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/probe/%d/run/%d", probe.ID, probe.LastResult.ID))
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
+ var templ_7745c5c3_Var6 string
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(probe.URL)
if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 103, Col: 81}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(probe.LastResult.LastModified)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 82, Col: 136}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</code></td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></td><td><button class=\"focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2\" hx-get=\"")
+ if probe.LastResult.CreatedAt.IsZero() {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500\">Not run yet</td>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500\"><a href=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var7 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/probe/%d/run/%d", probe.ID, probe.LastResult.ID))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7)))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var8 string
+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(probe.LastResult.CreatedAt.Format(time.RFC3339))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 107, Col: 204}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></td>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<td><button class=\"focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var8 string
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d/edit", probe.ID))
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d/edit", probe.ID))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 86, Col: 52}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 112, Col: 52}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -183,21 +207,21 @@ func probeEdit(probe Probe) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var9 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var9 == nil {
- templ_7745c5c3_Var9 = templ.NopComponent
+ templ_7745c5c3_Var10 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var10 == nil {
+ templ_7745c5c3_Var10 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr hx-trigger=\"cancel\" class=\"editing\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var10 string
- templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d", probe.ID))
+ var templ_7745c5c3_Var11 string
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d", probe.ID))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 111, Col: 84}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 137, Col: 84}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -205,12 +229,12 @@ func probeEdit(probe Probe) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var11 string
- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(probe.Name)
+ var templ_7745c5c3_Var12 string
+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(probe.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 112, Col: 43}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 138, Col: 43}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -218,12 +242,12 @@ func probeEdit(probe Probe) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var12 string
- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(probe.URL)
+ var templ_7745c5c3_Var13 string
+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(probe.URL)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 113, Col: 41}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 139, Col: 41}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -231,12 +255,12 @@ func probeEdit(probe Probe) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var13 string
- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d", probe.ID))
+ var templ_7745c5c3_Var14 string
+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d", probe.ID))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 117, Col: 47}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 143, Col: 47}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -244,12 +268,12 @@ func probeEdit(probe Probe) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var14 string
- templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d", probe.ID))
+ var templ_7745c5c3_Var15 string
+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/probe/%d", probe.ID))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 123, Col: 47}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 149, Col: 47}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -274,21 +298,21 @@ func probePage(probe Probe, history []ProbeResult) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var15 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var15 == nil {
- templ_7745c5c3_Var15 = templ.NopComponent
+ templ_7745c5c3_Var16 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var16 == nil {
+ templ_7745c5c3_Var16 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"px-4 sm:px-6 lg:px-8\"><div class=\"mt-8 flow-root\"><div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\"><div class=\"inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8\"><table class=\"min-w-full divide-y divide-gray-300\"><tbody><tr><td class=\"whitespace-nowrap py-2 pl-4 pr-3 font-medium text-gray-900 sm:pl-0\">Name</td><td class=\"whitespace-nowrap py-2 pl-4 pr-3 text-gray-900 sm:pl-0\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var16 string
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(probe.Name)
+ var templ_7745c5c3_Var17 string
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(probe.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 148, Col: 21}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 174, Col: 21}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -296,12 +320,12 @@ func probePage(probe Probe, history []ProbeResult) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var17 string
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(probe.CreatedAt.Format(time.RFC3339))
+ var templ_7745c5c3_Var18 string
+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(probe.CreatedAt.Format(time.RFC3339))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 160, Col: 47}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 186, Col: 47}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -309,12 +333,12 @@ func probePage(probe Probe, history []ProbeResult) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var18 string
- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(probe.URL)
+ var templ_7745c5c3_Var19 string
+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(probe.URL)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 172, Col: 26}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `probes.templ`, Line: 198, Col: 26}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -327,12 +351,12 @@ func probePage(probe Probe, history []ProbeResult) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var19 string