mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
feat: Add Microsoft Teams as notification provider (issue 260) (#313)
* feat: add Microsoft Teams notification provider * feat: Add Microsoft Teams as notification provider * fix: Update database migration * fix: Remove duplicate file 0058 db migration * add Microsoft Teams notification provider E2E tests * fix: externalize e2e testings and profile name trim (#318) * fix: externalize e2e testings * fix: externalize e2e tests. * refactor: improve e2e workflow. * chore: upgrade workflow action version. * fix: createEnv by adding emptyStringAsUndefined (#317) * chore: release 1.18.2 --------- Co-authored-by: killian-larcher <killian.larcher@soluce-technologies.com> Co-authored-by: Charles GTE <charles.gauthereau@soluce-technologies.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix(layout): guard undefined user.name in avatar fallbacks (#320) * fix(layout): guard undefined user.name in avatar fallbacks * fix: AUTH_DEFAULT_USER as zod email --------- Co-authored-by: charles-gauthereau <charles.gauthereau@soluce-technologies.com> * fix: profile name trim --------- Co-authored-by: killian-larcher <killian.larcher@soluce-technologies.com> Co-authored-by: Charles GTE <charles.gauthereau@soluce-technologies.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Michael Kutý <6du1ro.n@gmail.com> Co-authored-by: charles-gauthereau <charles.gauthereau@soluce-technologies.com> * chore: release 1.18.3 * chore: Update README.md (#323) * chore: Update README.md added unraid link * chore: Update README.md fix markdown * chore: release 1.18.4 * fix: teams.ts with timeout --------- Co-authored-by: Killian Larcher <98161034+KillianLarcher@users.noreply.github.com> Co-authored-by: killian-larcher <killian.larcher@soluce-technologies.com> Co-authored-by: Charles GTE <charles.gauthereau@soluce-technologies.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Michael Kutý <6du1ro.n@gmail.com> Co-authored-by: charles-gauthereau <charles.gauthereau@soluce-technologies.com> Co-authored-by: Shawn M <84647313+hyperion02@users.noreply.github.com>
This commit is contained in:
co-authored by
killian-larcher
Charles GTE
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Michael Kutý
Killian Larcher
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Shawn M
parent
777a8bba3c
commit
130c23dd5b
@@ -8,6 +8,7 @@ import {sendGotify} from "@/features/channel/notifications/gotify";
|
||||
import {sendNtfy} from "@/features/channel/notifications/ntfy";
|
||||
import {sendWebhook} from "@/features/channel/notifications/webhook";
|
||||
import {sendNextcloud} from "@/features/channel/notifications/nextcloud";
|
||||
import {sendTeams} from "@/features/channel/notifications/teams"
|
||||
|
||||
const handlers: Record<
|
||||
ProviderKind,
|
||||
@@ -21,6 +22,7 @@ const handlers: Record<
|
||||
ntfy: sendNtfy,
|
||||
webhook: sendWebhook,
|
||||
nextcloud: sendNextcloud,
|
||||
teams: sendTeams,
|
||||
};
|
||||
|
||||
export async function dispatchViaProvider(
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { PasswordInput } from "@/components/ui/password-input";
|
||||
|
||||
type NotifierTeamsFormProps = {
|
||||
form: UseFormReturn<any, any, any>;
|
||||
};
|
||||
|
||||
export const NotifierTeamsForm = ({ form }: NotifierTeamsFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<Separator className="my-1" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="config.teamsWebhook"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Teams Webhook URL *</FormLabel>
|
||||
<FormControl>
|
||||
<PasswordInput
|
||||
{...field}
|
||||
placeholder="e.g. https://xxxxx.logic.azure.com:443/xxxxx"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import {z} from "zod";
|
||||
|
||||
export const TeamsChannelConfigSchema = z.object({
|
||||
teamsWebhook: z.string().url("Must be a valid URL"),
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import type { EventPayload, DispatchResult } from '@/features/notifications/notifications.types';
|
||||
|
||||
|
||||
|
||||
export async function sendTeams(
|
||||
config: { teamsWebhook: string },
|
||||
payload: EventPayload
|
||||
): Promise<DispatchResult> {
|
||||
const { teamsWebhook: webhookUrl } = config;
|
||||
const levelColor: Record<string, string> = {
|
||||
critical: 'attention',
|
||||
warning: 'warning',
|
||||
info: 'good',
|
||||
};
|
||||
const accentColor = levelColor[payload.level] ?? 'default';
|
||||
const bodyItems: object[] = [
|
||||
{
|
||||
type: 'TextBlock',
|
||||
text: `**[${payload.level.toUpperCase()}] ${payload.title}**`,
|
||||
wrap: true,
|
||||
color: accentColor,
|
||||
size: 'Medium',
|
||||
weight: 'Bolder',
|
||||
},
|
||||
{
|
||||
type: 'TextBlock',
|
||||
text: payload.message,
|
||||
wrap: true,
|
||||
},
|
||||
];
|
||||
|
||||
if (payload.data) {
|
||||
bodyItems.push(
|
||||
{
|
||||
type: 'TextBlock',
|
||||
text: '**Data:**',
|
||||
wrap: true,
|
||||
weight: 'Bolder',
|
||||
},
|
||||
{
|
||||
type: 'TextBlock',
|
||||
text: JSON.stringify(payload.data, null, 2).substring(0, 1000),
|
||||
wrap: true,
|
||||
fontType: 'Monospace',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const adaptiveCard = {
|
||||
type: 'AdaptiveCard',
|
||||
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
|
||||
version: '1.2',
|
||||
body: bodyItems,
|
||||
};
|
||||
|
||||
const body = {
|
||||
type: 'message',
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'application/vnd.microsoft.card.adaptive',
|
||||
contentUrl: null,
|
||||
content: adaptiveCard,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 10_000);
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetch(webhookUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
signal: controller.signal,
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.text();
|
||||
throw new Error(`Teams error: ${res.status} ${err}`);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
provider: 'teams',
|
||||
message: 'Sent to Microsoft Teams',
|
||||
response: res.statusText,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user