Compare commits
4 Commits
notifcatio
...
install-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61a8c5fe8b | ||
|
|
c79136a06e | ||
|
|
a5590a438f | ||
|
|
1c196cae9f |
14
README.md
14
README.md
@@ -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).
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user