mirror of
https://github.com/airlabspl/uptimemonitor.git
synced 2025-08-14 20:29:16 +02:00
initial commit
This commit is contained in:
140
store/check_store.go
Normal file
140
store/check_store.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"uptimemonitor"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (s *Store) CreateCheck(ctx context.Context, check uptimemonitor.Check) (uptimemonitor.Check, error) {
|
||||
stmt := `INSERT INTO checks(uuid, monitor_id, status_code, response_time_ms, created_at) VALUES(?, ?, ?, ?, ?)`
|
||||
uuid := uuid.NewString()
|
||||
if check.CreatedAt.IsZero() {
|
||||
check.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return check, err
|
||||
}
|
||||
|
||||
defer tx.Rollback()
|
||||
|
||||
res, err := tx.ExecContext(ctx, stmt, uuid, check.MonitorID, check.StatusCode, check.ResponseTimeMs, check.CreatedAt)
|
||||
if err != nil {
|
||||
return check, err
|
||||
}
|
||||
|
||||
id, _ := res.LastInsertId()
|
||||
check.ID = id
|
||||
|
||||
stmt = `
|
||||
SELECT uptime, avg_response_time_ms, n, incidents_count
|
||||
FROM monitors
|
||||
WHERE id = ?
|
||||
`
|
||||
var n int64
|
||||
var uptime float32
|
||||
var avgResponseTimeMs int64
|
||||
var incidentsCount int64
|
||||
err = tx.QueryRowContext(ctx, stmt, check.MonitorID).Scan(&uptime, &avgResponseTimeMs, &n, &incidentsCount)
|
||||
if err != nil {
|
||||
return check, err
|
||||
}
|
||||
|
||||
if check.StatusCode >= 300 {
|
||||
incidentsCount++
|
||||
}
|
||||
|
||||
stmt = `
|
||||
UPDATE monitors
|
||||
SET uptime = ?, avg_response_time_ms = ?, n = ?, incidents_count = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
newIncidentCount := incidentsCount
|
||||
newN := n + 1
|
||||
newUptime := fmt.Sprintf("%.1f", float32(float32(newN-newIncidentCount)/float32(newN)*float32(100)))
|
||||
newAvgResponseTimeMs := (avgResponseTimeMs*n + check.ResponseTimeMs) / newN
|
||||
|
||||
_, err = tx.ExecContext(ctx, stmt, newUptime, newAvgResponseTimeMs, newN, newIncidentCount, check.MonitorID)
|
||||
if err != nil {
|
||||
return check, err
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
return check, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListChecks(ctx context.Context, monitorID int64, limit int) ([]uptimemonitor.Check, error) {
|
||||
stmt := `
|
||||
SELECT checks.id, checks.uuid, checks.monitor_id, checks.created_at,
|
||||
checks.status_code, checks.response_time_ms,
|
||||
monitors.id, monitors.uuid, monitors.url, monitors.created_at
|
||||
FROM checks
|
||||
LEFT JOIN monitors ON monitors.id = checks.monitor_id
|
||||
WHERE monitor_id = ?
|
||||
ORDER BY checks.id DESC
|
||||
LIMIT ?
|
||||
`
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, stmt, monitorID, limit)
|
||||
if err != nil {
|
||||
return []uptimemonitor.Check{}, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
var checks []uptimemonitor.Check
|
||||
|
||||
for rows.Next() {
|
||||
var c uptimemonitor.Check
|
||||
|
||||
if err := rows.Scan(
|
||||
&c.ID, &c.Uuid, &c.MonitorID, &c.CreatedAt,
|
||||
&c.StatusCode, &c.ResponseTimeMs,
|
||||
&c.Monitor.ID, &c.Monitor.Uuid, &c.Monitor.Url, &c.Monitor.CreatedAt,
|
||||
); err != nil {
|
||||
return []uptimemonitor.Check{}, err
|
||||
}
|
||||
|
||||
checks = append(checks, c)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return []uptimemonitor.Check{}, err
|
||||
}
|
||||
|
||||
return checks, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetCheckByID(ctx context.Context, id int64) (uptimemonitor.Check, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
checks.id, checks.uuid, checks.monitor_id, checks.status_code, checks.response_time_ms, checks.created_at,
|
||||
monitors.id, monitors.url, monitors.uuid, monitors.http_method, monitors.http_headers, monitors.http_body, monitors.created_at
|
||||
FROM checks
|
||||
LEFT JOIN monitors ON monitors.id = checks.monitor_id
|
||||
WHERE checks.id = ?
|
||||
`
|
||||
|
||||
var ch uptimemonitor.Check
|
||||
err := s.db.QueryRowContext(ctx, stmt, id).Scan(
|
||||
&ch.ID, &ch.Uuid, &ch.MonitorID, &ch.StatusCode, &ch.ResponseTimeMs, &ch.CreatedAt,
|
||||
&ch.Monitor.ID, &ch.Monitor.Url, &ch.Monitor.Uuid, &ch.Monitor.HttpMethod, &ch.Monitor.HttpHeaders, &ch.Monitor.HttpBody, &ch.Monitor.CreatedAt,
|
||||
)
|
||||
|
||||
return ch, err
|
||||
}
|
||||
|
||||
func (s *Store) DeleteOldChecks(ctx context.Context) error {
|
||||
stmt := `DELETE FROM checks WHERE created_at < ?`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, time.Now().Add(-time.Hour))
|
||||
|
||||
return err
|
||||
|
||||
}
|
8
store/embed.go
Normal file
8
store/embed.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package store
|
||||
|
||||
import "embed"
|
||||
|
||||
var (
|
||||
//go:embed migrations/*.sql
|
||||
FS embed.FS
|
||||
)
|
301
store/incident_store.go
Normal file
301
store/incident_store.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"uptimemonitor"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (s *Store) CreateIncident(ctx context.Context, incident uptimemonitor.Incident) (uptimemonitor.Incident, error) {
|
||||
stmt := `INSERT INTO incidents (uuid, monitor_id, status_text, status_code, response_time_ms, body, headers, req_method, req_url, req_headers, req_body, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id`
|
||||
|
||||
if incident.CreatedAt.IsZero() {
|
||||
incident.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
incident.Uuid = uuid.NewString()
|
||||
incident.StatusText = uptimemonitor.IncidentStatusOpen
|
||||
|
||||
res, err := s.db.ExecContext(ctx, stmt, incident.Uuid, incident.MonitorID, incident.StatusText, incident.StatusCode, incident.ResponseTimeMs, incident.Body, incident.Headers,
|
||||
incident.ReqMethod, incident.ReqUrl, incident.ReqHeaders, incident.ReqBody, incident.CreatedAt)
|
||||
if err != nil {
|
||||
return uptimemonitor.Incident{}, err
|
||||
}
|
||||
|
||||
id, err := res.LastInsertId()
|
||||
incident.ID = id
|
||||
|
||||
return incident, err
|
||||
}
|
||||
|
||||
func (s *Store) UpdateIncidentBodyAndHeaders(ctx context.Context, incident uptimemonitor.Incident, body, headers, reqMethod, reqUrl, reqHeaders, reqBody string) error {
|
||||
stmt := `
|
||||
UPDATE incidents
|
||||
SET body = ?, headers = ?, req_method = ?, req_url = ?, req_headers = ?, req_body = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, body, headers, reqMethod, reqUrl, reqHeaders, reqBody, incident.ID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) LastIncidentByStatusCode(ctx context.Context, monitorID int64, status string, statusCode int) (uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT id, uuid, monitor_id, status_text, status_code, response_time_ms, body, headers, created_at
|
||||
FROM incidents
|
||||
WHERE monitor_id = ? AND status_text = ? AND status_code = ?
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
row := s.db.QueryRowContext(ctx, stmt, monitorID, status, statusCode)
|
||||
|
||||
var incident uptimemonitor.Incident
|
||||
if err := row.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt,
|
||||
); err != nil {
|
||||
return uptimemonitor.Incident{}, err
|
||||
}
|
||||
|
||||
return incident, nil
|
||||
}
|
||||
|
||||
func (s *Store) LastOpenIncident(ctx context.Context, monitorID int64) (uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT id, uuid, monitor_id, status_text, status_code, response_time_ms, body, headers, created_at
|
||||
FROM incidents
|
||||
WHERE monitor_id = ? AND status_text = ?
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
row := s.db.QueryRowContext(ctx, stmt, monitorID, uptimemonitor.IncidentStatusOpen)
|
||||
|
||||
var incident uptimemonitor.Incident
|
||||
if err := row.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt,
|
||||
); err != nil {
|
||||
return uptimemonitor.Incident{}, err
|
||||
}
|
||||
|
||||
return incident, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListOpenIncidents(ctx context.Context) ([]uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT incidents.id, incidents.uuid, incidents.monitor_id,
|
||||
incidents.status_text, incidents.status_code, incidents.response_time_ms,
|
||||
incidents.body, incidents.headers, incidents.created_at,
|
||||
monitors.id, monitors.url, monitors.uuid, monitors.created_at
|
||||
FROM incidents
|
||||
JOIN monitors ON incidents.monitor_id = monitors.id
|
||||
WHERE incidents.status_text = ?
|
||||
ORDER BY incidents.id DESC
|
||||
`
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, stmt, uptimemonitor.IncidentStatusOpen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var incidents []uptimemonitor.Incident
|
||||
for rows.Next() {
|
||||
var incident uptimemonitor.Incident
|
||||
if err := rows.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt,
|
||||
&incident.Monitor.ID, &incident.Monitor.Url, &incident.Monitor.Uuid, &incident.Monitor.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
incidents = append(incidents, incident)
|
||||
}
|
||||
|
||||
return incidents, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListMonitorIncidents(ctx context.Context, id int64) ([]uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT incidents.id, incidents.uuid, incidents.monitor_id,
|
||||
incidents.status_text, incidents.status_code, incidents.response_time_ms,
|
||||
incidents.body, incidents.headers, incidents.created_at, incidents.resolved_at,
|
||||
monitors.id, monitors.url, monitors.uuid, monitors.created_at
|
||||
FROM incidents
|
||||
JOIN monitors ON incidents.monitor_id = monitors.id
|
||||
WHERE incidents.monitor_id = ?
|
||||
ORDER BY incidents.id DESC
|
||||
LIMIT 10
|
||||
`
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, stmt, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var incidents []uptimemonitor.Incident
|
||||
for rows.Next() {
|
||||
var incident uptimemonitor.Incident
|
||||
if err := rows.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt, &incident.ResolvedAt,
|
||||
&incident.Monitor.ID, &incident.Monitor.Url, &incident.Monitor.Uuid, &incident.Monitor.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
incidents = append(incidents, incident)
|
||||
}
|
||||
|
||||
return incidents, nil
|
||||
}
|
||||
func (s *Store) CountMonitorIncidents(ctx context.Context, id int64) int64 {
|
||||
stmt := `
|
||||
SELECT COUNT(*)
|
||||
FROM incidents
|
||||
WHERE monitor_id = ?
|
||||
`
|
||||
|
||||
var count int64
|
||||
s.db.QueryRowContext(ctx, stmt, id).Scan(&count)
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (s *Store) ListMonitorOpenIncidents(ctx context.Context, id int64) ([]uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT incidents.id, incidents.uuid, incidents.monitor_id,
|
||||
incidents.status_text, incidents.status_code, incidents.response_time_ms,
|
||||
incidents.body, incidents.headers, incidents.created_at,
|
||||
monitors.id, monitors.url, monitors.uuid, monitors.created_at
|
||||
FROM incidents
|
||||
JOIN monitors ON incidents.monitor_id = monitors.id
|
||||
WHERE incidents.monitor_id = ? AND incidents.status_text = ?
|
||||
ORDER BY incidents.id DESC
|
||||
`
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, stmt, id, uptimemonitor.IncidentStatusOpen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var incidents []uptimemonitor.Incident
|
||||
for rows.Next() {
|
||||
var incident uptimemonitor.Incident
|
||||
if err := rows.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt,
|
||||
&incident.Monitor.ID, &incident.Monitor.Url, &incident.Monitor.Uuid, &incident.Monitor.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
incidents = append(incidents, incident)
|
||||
}
|
||||
|
||||
return incidents, nil
|
||||
}
|
||||
|
||||
func (s *Store) ResolveIncident(ctx context.Context, incident uptimemonitor.Incident) error {
|
||||
stmt := `
|
||||
UPDATE incidents SET status_text = ?, resolved_at = ? WHERE id = ?
|
||||
`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, uptimemonitor.IncidentStatusResolved, time.Now(), incident.ID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) ResolveMonitorIncidents(ctx context.Context, monitor uptimemonitor.Monitor) error {
|
||||
stmt := `
|
||||
UPDATE incidents SET status_text = ?, resolved_at = ? WHERE monitor_id = ?
|
||||
`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, uptimemonitor.IncidentStatusResolved, time.Now(), monitor.ID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) DeleteOldIncidents(ctx context.Context) error {
|
||||
stmt := `DELETE FROM incidents WHERE created_at < ?`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, time.Now().Add(-time.Hour*24*7))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) DeleteIncident(ctx context.Context, id int64) error {
|
||||
stmt := `DELETE FROM incidents WHERE id = ?`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) GetIncidentByUuid(ctx context.Context, uuid string) (uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
incidents.id, incidents.uuid, incidents.monitor_id,
|
||||
incidents.status_text, incidents.status_code, incidents.response_time_ms,
|
||||
incidents.body, incidents.headers, incidents.created_at, incidents.resolved_at,
|
||||
incidents.req_method, incidents.req_url, incidents.req_headers, incidents.req_body,
|
||||
monitors.id, monitors.url, monitors.uuid, monitors.created_at
|
||||
FROM incidents
|
||||
LEFT JOIN monitors ON monitors.id = incidents.monitor_id
|
||||
WHERE incidents.uuid = ?
|
||||
`
|
||||
|
||||
row := s.db.QueryRowContext(ctx, stmt, uuid)
|
||||
|
||||
var incident uptimemonitor.Incident
|
||||
if err := row.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt, &incident.ResolvedAt,
|
||||
&incident.ReqMethod, &incident.ReqUrl, &incident.ReqHeaders, &incident.ReqBody,
|
||||
&incident.Monitor.ID, &incident.Monitor.Url, &incident.Monitor.Uuid, &incident.Monitor.CreatedAt,
|
||||
); err != nil {
|
||||
return incident, err
|
||||
}
|
||||
|
||||
return incident, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetIncidentByID(ctx context.Context, id int64) (uptimemonitor.Incident, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
incidents.id, incidents.uuid, incidents.monitor_id,
|
||||
incidents.status_text, incidents.status_code, incidents.response_time_ms,
|
||||
incidents.body, incidents.headers, incidents.created_at, incidents.resolved_at,
|
||||
incidents.req_method, incidents.req_url, incidents.req_headers, incidents.req_body,
|
||||
monitors.id, monitors.url, monitors.uuid, monitors.created_at
|
||||
FROM incidents
|
||||
LEFT JOIN monitors ON monitors.id = incidents.monitor_id
|
||||
WHERE incidents.id = ?
|
||||
`
|
||||
|
||||
row := s.db.QueryRowContext(ctx, stmt, id)
|
||||
|
||||
var incident uptimemonitor.Incident
|
||||
if err := row.Scan(
|
||||
&incident.ID, &incident.Uuid, &incident.MonitorID,
|
||||
&incident.StatusText, &incident.StatusCode, &incident.ResponseTimeMs,
|
||||
&incident.Body, &incident.Headers, &incident.CreatedAt, &incident.ResolvedAt,
|
||||
&incident.ReqMethod, &incident.ReqUrl, &incident.ReqHeaders, &incident.ReqBody,
|
||||
&incident.Monitor.ID, &incident.Monitor.Url, &incident.Monitor.Uuid, &incident.Monitor.CreatedAt,
|
||||
); err != nil {
|
||||
return incident, err
|
||||
}
|
||||
|
||||
return incident, nil
|
||||
}
|
68
store/migrations/00001_init.sql
Normal file
68
store/migrations/00001_init.sql
Normal file
@@ -0,0 +1,68 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uuid TEXT NOT NULL UNIQUE,
|
||||
user_id INTEGER,
|
||||
created_at DATETIME NOT NULL,
|
||||
expires_at DATETIME NOT NULL,
|
||||
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE monitors (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uuid TEXT NOT NULL UNIQUE,
|
||||
`url` TEXT NOT NULL,
|
||||
http_method TEXT NOT NULL,
|
||||
http_headers TEXT NOT NULL,
|
||||
http_body TEXT NOT NULL,
|
||||
webhook_url TEXT NOT NULL,
|
||||
webhook_method TEXT NOT NULL,
|
||||
webhook_headers TEXT NOT NULL,
|
||||
webhook_body TEXT NOT NULL,
|
||||
uptime FLOAT DEFAULT 0,
|
||||
avg_response_time_ms INTEGER DEFAULT 0,
|
||||
incidents_count INTEGER DEFAULT 0,
|
||||
n INTEGER DEFAULT 0,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE checks(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uuid TEXT NOT NULL UNIQUE,
|
||||
monitor_id INTEGER,
|
||||
created_at DATETIME NOT NULL,
|
||||
status_code INTEGER NOT NULL,
|
||||
response_time_ms INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY(monitor_id) REFERENCES monitors(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE incidents(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uuid TEXT NOT NULL UNIQUE,
|
||||
monitor_id INTEGER,
|
||||
status_text TEXT NOT NULL,
|
||||
status_code INTEGER NOT NULL,
|
||||
response_time_ms INTEGER NOT NULL,
|
||||
body TEXT,
|
||||
headers TEXT,
|
||||
req_method TEXT,
|
||||
req_url TEXT,
|
||||
req_headers TEXT,
|
||||
req_body TEXT,
|
||||
created_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME,
|
||||
|
||||
FOREIGN KEY(monitor_id) REFERENCES monitors(id) ON DELETE CASCADE
|
||||
);
|
||||
-- +goose StatementEnd
|
158
store/monitor_store.go
Normal file
158
store/monitor_store.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"uptimemonitor"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (s *Store) CountMonitors(ctx context.Context) int {
|
||||
stmt := `SELECT COUNT(*) FROM monitors`
|
||||
|
||||
var count int
|
||||
s.db.QueryRowContext(ctx, stmt).Scan(&count)
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (s *Store) CreateMonitor(ctx context.Context, monitor uptimemonitor.Monitor) (uptimemonitor.Monitor, error) {
|
||||
stmt := `
|
||||
INSERT INTO
|
||||
monitors(
|
||||
url, uuid,
|
||||
http_method, http_headers, http_body,
|
||||
webhook_url, webhook_method, webhook_headers, webhook_body,
|
||||
created_at
|
||||
)
|
||||
VALUES(?,?,?,?,?,?,?,?,?,?)
|
||||
`
|
||||
monitor.CreatedAt = time.Now()
|
||||
|
||||
uuid := uuid.NewString()
|
||||
res, err := s.db.ExecContext(
|
||||
ctx, stmt,
|
||||
monitor.Url, uuid, monitor.HttpMethod, monitor.HttpHeaders, monitor.HttpBody,
|
||||
monitor.WebhookUrl, monitor.WebhookMethod, monitor.WebhookHeaders, monitor.WebhookBody,
|
||||
monitor.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return monitor, err
|
||||
}
|
||||
|
||||
id, _ := res.LastInsertId()
|
||||
|
||||
monitor.ID = id
|
||||
monitor.Uuid = uuid
|
||||
return monitor, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListMonitors(ctx context.Context) ([]uptimemonitor.Monitor, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
id, url, uuid, created_at,
|
||||
http_method, http_body, http_headers,
|
||||
webhook_method, webhook_url, webhook_headers, webhook_body
|
||||
FROM monitors
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, stmt)
|
||||
if err != nil {
|
||||
return []uptimemonitor.Monitor{}, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var monitors []uptimemonitor.Monitor
|
||||
|
||||
for rows.Next() {
|
||||
var m uptimemonitor.Monitor
|
||||
if err := rows.Scan(
|
||||
&m.ID, &m.Url, &m.Uuid, &m.CreatedAt, &m.HttpMethod, &m.HttpBody, &m.HttpHeaders,
|
||||
&m.WebhookMethod, &m.WebhookUrl, &m.WebhookHeaders, &m.WebhookBody,
|
||||
); err != nil {
|
||||
return monitors, err
|
||||
}
|
||||
|
||||
monitors = append(monitors, m)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return monitors, err
|
||||
}
|
||||
|
||||
return monitors, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetMonitorByID(ctx context.Context, id int) (uptimemonitor.Monitor, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
id, url, uuid, http_method, http_headers, http_body,
|
||||
webhook_url, webhook_method, webhook_headers, webhook_body,
|
||||
uptime, avg_response_time_ms, n, incidents_count,
|
||||
created_at
|
||||
FROM monitors
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
`
|
||||
var m uptimemonitor.Monitor
|
||||
err := s.db.QueryRowContext(ctx, stmt, id).
|
||||
Scan(
|
||||
&m.ID, &m.Url, &m.Uuid, &m.HttpMethod, &m.HttpHeaders, &m.HttpBody,
|
||||
&m.WebhookUrl, &m.WebhookMethod, &m.WebhookHeaders, &m.WebhookBody,
|
||||
&m.Uptime, &m.AvgResponseTimeMs, &m.N, &m.IncidentsCount,
|
||||
&m.CreatedAt,
|
||||
)
|
||||
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (s *Store) GetMonitorByUuid(ctx context.Context, uuid string) (uptimemonitor.Monitor, error) {
|
||||
stmt := `
|
||||
SELECT
|
||||
id, url, uuid, http_method, http_headers, http_body,
|
||||
webhook_url, webhook_method, webhook_headers, webhook_body,
|
||||
uptime, avg_response_time_ms, n, incidents_count,
|
||||
created_at
|
||||
FROM monitors
|
||||
WHERE uuid = ?
|
||||
LIMIT 1
|
||||
`
|
||||
var m uptimemonitor.Monitor
|
||||
err := s.db.QueryRowContext(ctx, stmt, uuid).
|
||||
Scan(
|
||||
&m.ID, &m.Url, &m.Uuid, &m.HttpMethod, &m.HttpHeaders, &m.HttpBody,
|
||||
&m.WebhookUrl, &m.WebhookMethod, &m.WebhookHeaders, &m.WebhookBody,
|
||||
&m.Uptime, &m.AvgResponseTimeMs, &m.N, &m.IncidentsCount,
|
||||
&m.CreatedAt,
|
||||
)
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (s *Store) UpdateMonitor(ctx context.Context, monitor uptimemonitor.Monitor) error {
|
||||
stmt := `
|
||||
UPDATE monitors SET
|
||||
url = ?, http_method = ?, http_headers = ?, http_body = ?,
|
||||
webhook_url = ?, webhook_method = ?, webhook_headers = ?, webhook_body = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
_, err := s.db.ExecContext(
|
||||
ctx, stmt, monitor.Url, monitor.HttpMethod, monitor.HttpHeaders, monitor.HttpBody,
|
||||
monitor.WebhookUrl, monitor.WebhookMethod, monitor.WebhookHeaders, monitor.WebhookBody,
|
||||
monitor.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) DeleteMonitor(ctx context.Context, id int64) error {
|
||||
stmt := `DELETE FROM monitors WHERE id = ?`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, id)
|
||||
return err
|
||||
}
|
54
store/session_store.go
Normal file
54
store/session_store.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"uptimemonitor"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (s *Store) CreateSession(ctx context.Context, session uptimemonitor.Session) (uptimemonitor.Session, error) {
|
||||
stmt := `INSERT INTO sessions (uuid, user_id, created_at, expires_at) VALUES(?, ?, ?, ?)`
|
||||
uuid := uuid.NewString()
|
||||
session.CreatedAt = time.Now()
|
||||
|
||||
res, err := s.db.ExecContext(ctx, stmt, uuid, session.UserID, session.CreatedAt, session.ExpiresAt)
|
||||
if err != nil {
|
||||
return session, err
|
||||
}
|
||||
|
||||
id, _ := res.LastInsertId()
|
||||
|
||||
session.ID = id
|
||||
session.Uuid = uuid
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetSessionByUuid(ctx context.Context, uuid string) (uptimemonitor.Session, error) {
|
||||
stmt := `
|
||||
SELECT sessions.id, sessions.user_id, sessions.created_at, sessions.expires_at,
|
||||
users.id, users.name, users.email, users.created_at
|
||||
FROM sessions
|
||||
LEFT JOIN users ON users.id = sessions.user_id
|
||||
WHERE uuid = ?
|
||||
LIMIT 1
|
||||
`
|
||||
var session uptimemonitor.Session
|
||||
|
||||
err := s.db.QueryRowContext(ctx, stmt, uuid).Scan(
|
||||
&session.ID, &session.UserID, &session.CreatedAt, &session.ExpiresAt,
|
||||
&session.User.ID, &session.User.Name, &session.User.Email, &session.User.CreatedAt,
|
||||
)
|
||||
|
||||
return session, err
|
||||
}
|
||||
|
||||
func (s *Store) RemoveSessionByID(ctx context.Context, id int64) error {
|
||||
stmt := `
|
||||
DELETE FROM sessions WHERE id = ?
|
||||
`
|
||||
|
||||
_, err := s.db.ExecContext(ctx, stmt, id)
|
||||
return err
|
||||
}
|
51
store/store.go
Normal file
51
store/store.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/pressly/goose/v3"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func New(dsn string) *Store {
|
||||
db, err := sql.Open("sqlite", dsn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("PRAGMA journal_mode=WAL;")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to enable WAL mode: %v", err))
|
||||
}
|
||||
|
||||
db.Exec("PRAGMA foreign_keys = ON;")
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
db.SetMaxIdleConns(1)
|
||||
db.SetConnMaxLifetime(0)
|
||||
|
||||
goose.SetBaseFS(FS)
|
||||
if err := goose.SetDialect("sqlite"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := goose.Up(db, "migrations"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &Store{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) DB() *sql.DB {
|
||||
return s.db
|
||||
}
|
48
store/user_store.go
Normal file
48
store/user_store.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"uptimemonitor"
|
||||
)
|
||||
|
||||
func (s *Store) CountUsers(ctx context.Context) (int, error) {
|
||||
stmt := `SELECT COUNT(*) FROM users`
|
||||
|
||||
var count int
|
||||
if err := s.db.QueryRowContext(ctx, stmt).Scan(&count); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s *Store) CreateUser(ctx context.Context, user uptimemonitor.User) (uptimemonitor.User, error) {
|
||||
stmt := `INSERT INTO users (name, email, password_hash, created_at) VALUES (?, ?, ?, ?) RETURNING id`
|
||||
user.CreatedAt = time.Now()
|
||||
|
||||
res, err := s.db.ExecContext(ctx, stmt, user.Name, user.Email, user.PasswordHash, user.CreatedAt)
|
||||
if err != nil {
|
||||
return uptimemonitor.User{}, err
|
||||
}
|
||||
|
||||
id, _ := res.LastInsertId()
|
||||
|
||||
user.ID = id
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetUserByEmail(ctx context.Context, email string) (uptimemonitor.User, error) {
|
||||
stmt := `SELECT id, name, email, password_hash, created_at FROM users WHERE email = ? LIMIT 1`
|
||||
|
||||
row := s.db.QueryRowContext(ctx, stmt, email)
|
||||
|
||||
var user uptimemonitor.User
|
||||
if err := row.Scan(
|
||||
&user.ID, &user.Name, &user.Email, &user.PasswordHash, &user.CreatedAt,
|
||||
); err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
Reference in New Issue
Block a user