Skip to content

Commit

Permalink
feat: add slack as a critical alert channel (#4319)
Browse files Browse the repository at this point in the history
* feat: add slack as a critical alert channel

* update ee ref
  • Loading branch information
HugoCasa authored Sep 4, 2024
1 parent 2f37501 commit a7a08cf
Show file tree
Hide file tree
Showing 13 changed files with 280 additions and 19 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/ee-repo-ref.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
04a34e95a377afdc45cf4f354d5009f02260f3b1
3d8ce9f1f15de94a40a0c349c7769598041a0d8a
30 changes: 30 additions & 0 deletions backend/windmill-api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2587,6 +2587,36 @@ paths:
schema:
type: string


/oauth/connect_slack_callback:
post:
summary: connect slack callback instance
operationId: connectSlackCallbackInstance
tags:
- oauth
requestBody:
description: code endpoint
required: true
content:
application/json:
schema:
type: object
properties:
code:
type: string
state:
type: string
required:
- code
- state
responses:
"200":
description: success message
content:
text/plain:
schema:
type: string

/oauth/connect_callback/{client_name}:
post:
summary: connect callback
Expand Down
12 changes: 9 additions & 3 deletions backend/windmill-common/src/ee.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[cfg(feature = "enterprise")]
use crate::db::DB;
use crate::ee::LicensePlan::Community;
#[cfg(feature = "enterprise")]
Expand Down Expand Up @@ -27,10 +28,15 @@ pub async fn get_license_plan() -> LicensePlan {
#[serde(untagged)]
pub enum CriticalErrorChannel {}

pub enum CriticalAlertKind {
#[cfg(feature = "enterprise")]
CriticalError,
#[cfg(feature = "enterprise")]
RecoveredCriticalError,
}

#[cfg(feature = "enterprise")]
pub async fn send_critical_error(_error_message: String) {}
#[cfg(feature = "enterprise")]
pub async fn send_recovered_critical_error(_error_message: String) {}
pub async fn send_critical_alert(_error_message: String, _db: &DB, _kind: CriticalAlertKind) {}

#[cfg(feature = "enterprise")]
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
Expand Down
2 changes: 2 additions & 0 deletions backend/windmill-common/src/oauth2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub type HmacSha256 = Hmac<Sha256>;

pub const WORKSPACE_SLACK_BOT_TOKEN_PATH: &str = "f/slack_bot/bot_token";

pub const GLOBAL_SLACK_BOT_TOKEN_PATH: &str = "f/slack_bot/global_bot_token";

lazy_static::lazy_static! {

pub static ref REQUIRE_PREEXISTING_USER_FOR_OAUTH: AtomicBool = AtomicBool::new(std::env::var("REQUIRE_PREEXISTING_USER_FOR_OAUTH")
Expand Down
15 changes: 12 additions & 3 deletions backend/windmill-common/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

use crate::ee::LICENSE_KEY_ID;
#[cfg(feature = "enterprise")]
use crate::ee::{send_critical_error, send_recovered_critical_error};
use crate::ee::{send_critical_alert, CriticalAlertKind};
use crate::error::{to_anyhow, Error, Result};
use crate::global_settings::UNIQUE_ID_SETTING;
use crate::server::Smtp;
Expand All @@ -19,6 +19,7 @@ use git_version::git_version;
use mail_send::mail_builder::MessageBuilder;
use mail_send::SmtpClientBuilder;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use sqlx::{Pool, Postgres};
Expand All @@ -29,6 +30,14 @@ pub const DEFAULT_PER_PAGE: usize = 1000;
pub const GIT_VERSION: &str =
git_version!(args = ["--tag", "--always"], fallback = "unknown-version");

lazy_static::lazy_static! {
pub static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new()
.user_agent("windmill/beta")
.timeout(std::time::Duration::from_secs(20))
.connect_timeout(std::time::Duration::from_secs(10))
.build().unwrap();
}

#[derive(Deserialize)]
pub struct Pagination {
pub page: Option<usize>,
Expand Down Expand Up @@ -279,7 +288,7 @@ pub async fn report_critical_error(error_message: String, _db: DB) -> () {
}

#[cfg(feature = "enterprise")]
send_critical_error(error_message).await;
send_critical_alert(error_message, &_db, CriticalAlertKind::CriticalError).await;
}

pub async fn report_recovered_critical_error(message: String, _db: DB) -> () {
Expand All @@ -295,5 +304,5 @@ pub async fn report_recovered_critical_error(message: String, _db: DB) -> () {
tracing::error!("Failed to save critical error to database: {}", err);
}
#[cfg(feature = "enterprise")]
send_recovered_critical_error(message).await;
send_critical_alert(message, &_db, CriticalAlertKind::RecoveredCriticalError).await;
}
92 changes: 81 additions & 11 deletions frontend/src/lib/components/InstanceSettings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@
import { capitalize, classNames } from '$lib/utils'
import { enterpriseLicense } from '$lib/stores'
import CustomOauth from './CustomOauth.svelte'
import { AlertCircle, AlertTriangle, BadgeCheck, Info, Plus, X, BadgeX } from 'lucide-svelte'
import {
AlertCircle,
AlertTriangle,
BadgeCheck,
Info,
Plus,
X,
BadgeX,
Slack
} from 'lucide-svelte'
import CustomSso from './CustomSso.svelte'
import AuthentikSetting from '$lib/components/AuthentikSetting.svelte'
import AutheliaSetting from '$lib/components/AutheliaSetting.svelte'
Expand All @@ -27,6 +36,8 @@
import { fade } from 'svelte/transition'
import Popover from './Popover.svelte'
import { base } from '$lib/base'
export let tab: string = 'Core'
export let hideTabs: boolean = false
export let hideSave: boolean = false
Expand Down Expand Up @@ -245,6 +256,11 @@
>Setting SMTP unlocks sending emails upon adding new users to the workspace or the
instance.</div
>
{:else if category == 'Slack'}
<div class="text-secondary pb-4 text-xs">
Connecting your instance to a Slack workspace enables critical alerts to be sent to a
Slack channel.
</div>
{:else if category == 'Telemetry'}
<div class="text-secondary pb-4 text-xs">
Anonymous usage data is collected to help improve Windmill.
Expand Down Expand Up @@ -654,21 +670,46 @@
{#if $enterpriseLicense && Array.isArray(values[setting.key])}
{#each values[setting.key] ?? [] as v, i}
<div class="flex max-w-md mt-1 gap-2 w-full items-center">
<select>
<option value="email">Email</option>
</select>
<input
type="email"
placeholder="Email address"
on:input={(e) => {
<select
on:change={(e) => {
if (e.target?.['value']) {
values[setting.key][i] = {
email: e.target['value']
[e.target['value']]: ''
}
}
}}
value={v?.email ?? ''}
/>
value={v && 'slack_channel' in v ? 'slack_channel' : 'email'}
>
<option value="email">Email</option>
<option value="slack_channel">Slack</option>
</select>
{#if v && 'slack_channel' in v}
<input
type="text"
placeholder="Slack channel"
on:input={(e) => {
if (e.target?.['value']) {
values[setting.key][i] = {
slack_channel: e.target['value']
}
}
}}
value={v?.slack_channel ?? ''}
/>
{:else}
<input
type="email"
placeholder="Email address"
on:input={(e) => {
if (e.target?.['value']) {
values[setting.key][i] = {
email: e.target['value']
}
}
}}
value={v?.email ?? ''}
/>
{/if}
<button
transition:fade|local={{ duration: 100 }}
class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover"
Expand Down Expand Up @@ -707,6 +748,35 @@
Add item
</Button>
</div>
{:else if setting.fieldType == 'slack_connect'}
<div class="flex flex-col items-start self-start">
{#if values[setting.key] && 'team_name' in values[setting.key]}
<div class="text-sm">
Connected to <code>{values[setting.key]['team_name']}</code>
</div>
<Button
size="sm"
endIcon={{ icon: Slack }}
btnClasses="mt-2"
variant="border"
on:click={async () => {
values[setting.key] = undefined
}}
>
Disconnect Slack
</Button>
{:else}
<Button
size="xs"
color="dark"
href="{base}/api/oauth/connect_slack?instance=true"
startIcon={{ icon: Slack }}
disabled={!$enterpriseLicense}
>
Connect to Slack
</Button>
{/if}
</div>
{:else if setting.fieldType == 'object_store_config'}
<ObjectStoreConfigSettings bind:bucket_config={values[setting.key]} />
<div class="mb-6" />
Expand Down
12 changes: 11 additions & 1 deletion frontend/src/lib/components/instanceSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface Setting {
| 'license_key'
| 'object_store_config'
| 'critical_error_channels'
| 'slack_connect'
storage: SettingStorage
isValid?: (value: any) => boolean
error?: string
Expand Down Expand Up @@ -122,7 +123,7 @@ export const settings: Record<string, Setting[]> = {
{
label: 'Critical Alert Channels',
description:
'Channels to send critical alerts to. SMTP must be configured for the email channel.',
'Channels to send critical alerts to. SMTP must be configured for the email channel. A Slack workspace must be connected to the instance for the Slack channel.',
key: 'critical_error_channels',
fieldType: 'critical_error_channels',
storage: 'setting',
Expand Down Expand Up @@ -229,6 +230,15 @@ export const settings: Record<string, Setting[]> = {
storage: 'config'
}
],
Slack: [
{
label: 'Slack',
key: 'slack',
fieldType: 'slack_connect',
storage: 'setting',
ee_only: ''
}
],
'SCIM/SAML': [
{
label: 'SCIM Token',
Expand Down
Loading

0 comments on commit a7a08cf

Please sign in to comment.