Compare commits

...

5 Commits

Author SHA1 Message Date
KS Jannette
6590cbf4be add sdk
Some checks are pending
check / check (push) Waiting to run
2026-03-01 04:31:32 -05:00
KS Jannette
61a8c5fe8b hottie
Some checks are pending
check / check (push) Waiting to run
2026-03-01 03:54:06 -05:00
S Jannette
c79136a06e Merge pull request #6 from kjannette/db-updates-to-keep-up-withe-alerts-functionality
notifcation dongif setttings - save to DB
2026-03-01 03:37:04 -05:00
KS Jannette
a5590a438f notifcation dongif setttings - save to DB
Some checks are pending
check / check (push) Waiting to run
2026-03-01 03:36:32 -05:00
S Jannette
1c196cae9f Merge pull request #5 from kjannette/notifcations-logic-upgrade
Building out notification update channels
2026-03-01 03:23:31 -05:00
6 changed files with 28 additions and 22 deletions

View File

@@ -99,20 +99,6 @@ to the database, and dispatches Discord notifications.
Firebase, communicates with the API via fetch, and renders the address/alert Firebase, communicates with the API via fetch, and renders the address/alert
management UI. management UI.
## TODO
- [ ] Switch frontend package manager from npm to yarn (per repo policy)
- [ ] Rename `go.mod` module path from `github.com/kjannette/koin-ping` to
`sneak.berlin/go/koin-ping` (per repo policy)
- [ ] Move database migrations to `internal/db/migrations/` and embed them in
the binary
- [ ] Pin all Docker base images by `@sha256` in Dockerfile
- [ ] Install `golangci-lint` locally and pass `make lint`
- [ ] Add vitest unit tests for the React frontend
- [ ] Add a `make db-reset` / `make migrate` target for schema management
- [ ] Set HTTP server read/write timeouts (currently unbounded)
- [ ] Replace `log.Printf` calls with structured `log/slog`
## License ## License
MIT. See [LICENSE](LICENSE). MIT. See [LICENSE](LICENSE).

View File

@@ -18,9 +18,9 @@ import (
) )
const ( const (
// serverReadTimeoutSeconds is the maximum duration to read a request. // max duration to read a request.
serverReadTimeoutSeconds = 5 serverReadTimeoutSeconds = 5
// serverWriteTimeoutSeconds is the maximum duration to write a response. // maxiduration to write a response.
serverWriteTimeoutSeconds = 10 serverWriteTimeoutSeconds = 10
) )
@@ -74,7 +74,7 @@ func main() {
mux.Handle("DELETE "+b+"/addresses/{addressId}", mux.Handle("DELETE "+b+"/addresses/{addressId}",
middleware.Authenticate(http.HandlerFunc(addressHandler.Remove))) middleware.Authenticate(http.HandlerFunc(addressHandler.Remove)))
// Authenticated routes alert rules // Authenticated routes for alert rules
mux.Handle("POST "+b+"/addresses/{addressId}/alerts", mux.Handle("POST "+b+"/addresses/{addressId}/alerts",
middleware.Authenticate(http.HandlerFunc(alertRuleHandler.Create))) middleware.Authenticate(http.HandlerFunc(alertRuleHandler.Create)))
mux.Handle("GET "+b+"/addresses/{addressId}/alerts", mux.Handle("GET "+b+"/addresses/{addressId}/alerts",

View File

@@ -42,6 +42,7 @@ require (
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/joho/godotenv v1.5.1 // indirect github.com/joho/godotenv v1.5.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/resend/resend-go/v3 v3.1.1 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect

View File

@@ -92,6 +92,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/resend/resend-go/v3 v3.1.1 h1:Uwpf/tZU+O/r/3nMWE6zUAMIG9dX/vTBS3wlQzYJKSw=
github.com/resend/resend-go/v3 v3.1.1/go.mod h1:iI7VA0NoGjWvsNii5iNC5Dy0llsI3HncXPejhniYzwE=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View File

@@ -45,11 +45,11 @@ func (m *NotificationConfigModel) UpsertConfig(ctx context.Context, userID strin
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
ON CONFLICT (user_id) ON CONFLICT (user_id)
DO UPDATE SET DO UPDATE SET
discord_webhook_url = COALESCE($2, user_notification_configs.discord_webhook_url), discord_webhook_url = $2,
telegram_chat_id = COALESCE($3, user_notification_configs.telegram_chat_id), telegram_chat_id = $3,
telegram_bot_token = COALESCE($4, user_notification_configs.telegram_bot_token), telegram_bot_token = $4,
email = COALESCE($5, user_notification_configs.email), email = $5,
slack_webhook_url = COALESCE($6, user_notification_configs.slack_webhook_url), slack_webhook_url = $6,
notification_enabled = $7, notification_enabled = $7,
updated_at = NOW() updated_at = NOW()
RETURNING user_id, discord_webhook_url, telegram_chat_id, telegram_bot_token, RETURNING user_id, discord_webhook_url, telegram_chat_id, telegram_bot_token,

View File

@@ -65,6 +65,7 @@ export default function Alerts() {
const [testingChannels, setTestingChannels] = useState(false); const [testingChannels, setTestingChannels] = useState(false);
const [settingUpEmail, setSettingUpEmail] = useState(false); const [settingUpEmail, setSettingUpEmail] = useState(false);
const [sendingDigest, setSendingDigest] = useState(false); const [sendingDigest, setSendingDigest] = useState(false);
const [hasExistingConfig, setHasExistingConfig] = useState(false);
useEffect(() => { useEffect(() => {
async function fetchData() { async function fetchData() {
@@ -86,6 +87,14 @@ export default function Alerts() {
setTelegramChatId(configData.telegram_chat_id || ""); setTelegramChatId(configData.telegram_chat_id || "");
setEmail(configData.email || ""); setEmail(configData.email || "");
setSlackWebhookUrl(configData.slack_webhook_url || ""); setSlackWebhookUrl(configData.slack_webhook_url || "");
const hasSaved =
!!configData.discord_webhook_url ||
!!configData.telegram_bot_token ||
!!configData.telegram_chat_id ||
!!configData.email ||
!!configData.slack_webhook_url;
setHasExistingConfig(hasSaved);
} catch (err) { } catch (err) {
setError(err.message); setError(err.message);
console.error("Failed to fetch data:", err); console.error("Failed to fetch data:", err);
@@ -155,6 +164,13 @@ export default function Alerts() {
} }
async function handleSaveNotificationConfig() { async function handleSaveNotificationConfig() {
if (hasExistingConfig) {
const confirmed = window.confirm(
"This will overwrite your previously saved notification settings. Continue?",
);
if (!confirmed) return;
}
try { try {
setNotificationLoading(true); setNotificationLoading(true);
setNotificationError(null); setNotificationError(null);
@@ -170,6 +186,7 @@ export default function Alerts() {
}; };
await updateNotificationConfig(config); await updateNotificationConfig(config);
setHasExistingConfig(true);
setNotificationSuccess("Notification settings saved!"); setNotificationSuccess("Notification settings saved!");
setTimeout(() => setNotificationSuccess(null), 3000); setTimeout(() => setNotificationSuccess(null), 3000);
} catch (err) { } catch (err) {