mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: implement test connection for all the providers
This commit is contained in:
@@ -19,10 +19,9 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { api } from "@/utils/api";
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useFieldArray, useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
@@ -72,6 +71,7 @@ const mySchema = z.discriminatedUnion("type", [
|
||||
smtpPort: z.string().min(1),
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
fromAddress: z.string().min(1),
|
||||
toAddresses: z.array(z.string()).min(1),
|
||||
})
|
||||
.merge(baseDatabaseSchema),
|
||||
@@ -101,6 +101,8 @@ type AddNotification = z.infer<typeof mySchema>;
|
||||
export const AddNotification = () => {
|
||||
const utils = api.useUtils();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { mutateAsync: testConnection, isLoading: isLoadingConnection } =
|
||||
api.notification.testConnection.useMutation();
|
||||
const slackMutation = api.notification.createSlack.useMutation();
|
||||
const telegramMutation = api.notification.createTelegram.useMutation();
|
||||
const discordMutation = api.notification.createDiscord.useMutation();
|
||||
@@ -114,11 +116,23 @@ export const AddNotification = () => {
|
||||
},
|
||||
resolver: zodResolver(mySchema),
|
||||
});
|
||||
const type = form.watch("type");
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "toAddresses",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (type === "email") {
|
||||
append("");
|
||||
}
|
||||
}, [type, append]);
|
||||
|
||||
useEffect(() => {
|
||||
form.reset();
|
||||
}, [form, form.reset, form.formState.isSubmitSuccessful]);
|
||||
|
||||
const type = form.watch("type");
|
||||
const activeMutation = {
|
||||
slack: slackMutation,
|
||||
telegram: telegramMutation,
|
||||
@@ -178,6 +192,7 @@ export const AddNotification = () => {
|
||||
smtpPort: data.smtpPort,
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
fromAddress: data.fromAddress,
|
||||
toAddresses: data.toAddresses,
|
||||
name: data.name,
|
||||
});
|
||||
@@ -339,7 +354,7 @@ export const AddNotification = () => {
|
||||
<FormLabel>Bot Token</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
placeholder="6660491268:AAFMGmajZOVewpMNZCgJr5H7cpXpoZPgvXw"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -356,7 +371,7 @@ export const AddNotification = () => {
|
||||
<FormItem>
|
||||
<FormLabel>Chat ID</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Chat ID" {...field} />
|
||||
<Input placeholder="431231869" {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
@@ -452,8 +467,21 @@ export const AddNotification = () => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="fromAddress"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>From Address</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="from@example.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* <FormField
|
||||
control={form.control}
|
||||
name="toAddresses"
|
||||
render={({ field }) => (
|
||||
@@ -466,7 +494,54 @@ export const AddNotification = () => {
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
/> */}
|
||||
<div className="flex flex-col gap-2 pt-2">
|
||||
<FormLabel>To Addresses</FormLabel>
|
||||
|
||||
{fields.map((field, index) => (
|
||||
<div
|
||||
key={field.id}
|
||||
className="flex flex-row gap-2 w-full"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`toAddresses.${index}`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="email@example.com"
|
||||
className="w-full"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
remove(index);
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
append("");
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -561,7 +636,35 @@ export const AddNotification = () => {
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogFooter className="flex flex-row gap-2 !justify-between w-full">
|
||||
<Button
|
||||
isLoading={isLoadingConnection}
|
||||
variant="secondary"
|
||||
onClick={async () => {
|
||||
await testConnection({
|
||||
webhookUrl: form.getValues("webhookUrl"),
|
||||
channel: form.getValues("channel"),
|
||||
notificationType: type,
|
||||
botToken: form.getValues("botToken"),
|
||||
chatId: form.getValues("chatId"),
|
||||
//
|
||||
smtpPort: form.getValues("smtpPort"),
|
||||
smtpServer: form.getValues("smtpServer"),
|
||||
username: form.getValues("username"),
|
||||
password: form.getValues("password"),
|
||||
toAddresses: form.getValues("toAddresses"),
|
||||
fromAddress: form.getValues("fromAddress"),
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success("Connection Success");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error to connect the provider");
|
||||
});
|
||||
}}
|
||||
>
|
||||
Send Test
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={form.formState.isSubmitting}
|
||||
form="hook-form"
|
||||
|
||||
7
drizzle/0020_ambitious_edwin_jarvis.sql
Normal file
7
drizzle/0020_ambitious_edwin_jarvis.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."notificationType" AS ENUM('slack', 'telegram', 'discord', 'email');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "notification" ADD COLUMN "notificationType" "notificationType" NOT NULL;
|
||||
1
drizzle/0021_nervous_dragon_lord.sql
Normal file
1
drizzle/0021_nervous_dragon_lord.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "email" ADD COLUMN "fromAddress" text NOT NULL;
|
||||
2913
drizzle/meta/0020_snapshot.json
Normal file
2913
drizzle/meta/0020_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2919
drizzle/meta/0021_snapshot.json
Normal file
2919
drizzle/meta/0021_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -141,6 +141,20 @@
|
||||
"when": 1720507012671,
|
||||
"tag": "0019_careless_hardball",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"version": "6",
|
||||
"when": 1720762222675,
|
||||
"tag": "0020_ambitious_edwin_jarvis",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"version": "6",
|
||||
"when": 1720768664067,
|
||||
"tag": "0021_nervous_dragon_lord",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
316
package.json
316
package.json
@@ -1,159 +1,161 @@
|
||||
{
|
||||
"name": "dokploy",
|
||||
"version": "v0.3.1",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "npm run build-server && npm run build-next",
|
||||
"start": "node dist/server.mjs",
|
||||
"build-server": "tsx esbuild.config.ts",
|
||||
"build-next": "next build",
|
||||
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
|
||||
"reset-password": "node dist/reset-password.mjs",
|
||||
"dev": "tsx watch -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
|
||||
"studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
|
||||
"migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts",
|
||||
"migration:run": "tsx -r dotenv/config migration.ts",
|
||||
"migration:up": "drizzle-kit up --config ./server/db/drizzle.config.ts",
|
||||
"migration:drop": "drizzle-kit drop --config ./server/db/drizzle.config.ts",
|
||||
"db:push": "drizzle-kit --config ./server/db/drizzle.config.ts",
|
||||
"db:truncate": "tsx -r dotenv/config ./server/db/reset.ts",
|
||||
"db:studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
|
||||
"lint": "biome lint",
|
||||
"db:seed": "tsx -r dotenv/config ./server/db/seed.ts",
|
||||
"db:clean": "tsx -r dotenv/config ./server/db/reset.ts",
|
||||
"docker:build": "./docker/build.sh",
|
||||
"docker:push": "./docker/push.sh",
|
||||
"docker:build:canary": "./docker/build.sh canary",
|
||||
"docker:push:canary": "./docker/push.sh canary",
|
||||
"version": "echo $(node -p \"require('./package.json').version\")",
|
||||
"test": "vitest --config __test__/vitest.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.515.0",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-yaml": "^6.1.1",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/legacy-modes": "6.4.0",
|
||||
"@dokploy/trpc-openapi": "0.0.4",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@lucia-auth/adapter-drizzle": "1.0.7",
|
||||
"@octokit/auth-app": "^6.0.4",
|
||||
"@octokit/webhooks": "^13.2.7",
|
||||
"@radix-ui/react-accordion": "1.1.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-radio-group": "^1.1.3",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-table": "^8.16.0",
|
||||
"@trpc/client": "^10.43.6",
|
||||
"@trpc/next": "^10.43.6",
|
||||
"@trpc/react-query": "^10.43.6",
|
||||
"@trpc/server": "^10.43.6",
|
||||
"@uiw/codemirror-theme-github": "^4.22.1",
|
||||
"@uiw/react-codemirror": "^4.22.1",
|
||||
"@xterm/addon-attach": "0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"bcrypt": "5.1.1",
|
||||
"bl": "6.0.11",
|
||||
"boxen": "^7.1.1",
|
||||
"bullmq": "5.4.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"cmdk": "^0.2.0",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"date-fns": "3.6.0",
|
||||
"dockerode": "4.0.2",
|
||||
"dockerode-compose": "^1.4.0",
|
||||
"dockerstats": "2.4.2",
|
||||
"dotenv": "16.4.5",
|
||||
"drizzle-orm": "^0.30.8",
|
||||
"drizzle-zod": "0.5.1",
|
||||
"hi-base32": "^0.5.1",
|
||||
"input-otp": "^1.2.4",
|
||||
"js-yaml": "4.1.0",
|
||||
"k6": "^0.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"lucia": "^3.0.1",
|
||||
"lucide-react": "^0.312.0",
|
||||
"nanoid": "3",
|
||||
"next": "^14.1.3",
|
||||
"next-themes": "^0.2.1",
|
||||
"node-os-utils": "1.3.7",
|
||||
"node-pty": "1.0.0",
|
||||
"node-schedule": "2.1.1",
|
||||
"octokit": "3.1.2",
|
||||
"otpauth": "^9.2.3",
|
||||
"postgres": "3.4.4",
|
||||
"public-ip": "6.0.2",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.49.3",
|
||||
"recharts": "^2.12.3",
|
||||
"slugify": "^1.6.6",
|
||||
"sonner": "^1.4.0",
|
||||
"superjson": "^2.2.1",
|
||||
"swagger-ui-react": "^5.17.14",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tar-fs": "3.0.5",
|
||||
"use-resize-observer": "9.1.0",
|
||||
"ws": "8.16.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
"zod": "^3.23.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.7.1",
|
||||
"@types/bcrypt": "5.0.2",
|
||||
"@types/dockerode": "3.3.23",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/lodash": "4.17.4",
|
||||
"@types/node": "^18.17.0",
|
||||
"@types/node-os-utils": "1.3.4",
|
||||
"@types/node-schedule": "2.1.6",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/swagger-ui-react": "^4.18.3",
|
||||
"@types/tar-fs": "2.0.4",
|
||||
"@types/ws": "8.5.10",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"drizzle-kit": "^0.21.1",
|
||||
"esbuild": "0.20.2",
|
||||
"localtunnel": "2.0.2",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.4.2",
|
||||
"vite-tsconfig-paths": "4.3.2",
|
||||
"vitest": "^1.6.0",
|
||||
"xterm-readline": "1.1.1"
|
||||
},
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.25.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0",
|
||||
"pnpm": ">=8.15.4"
|
||||
}
|
||||
"name": "dokploy",
|
||||
"version": "v0.3.1",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "npm run build-server && npm run build-next",
|
||||
"start": "node dist/server.mjs",
|
||||
"build-server": "tsx esbuild.config.ts",
|
||||
"build-next": "next build",
|
||||
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
|
||||
"reset-password": "node dist/reset-password.mjs",
|
||||
"dev": "tsx watch -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
|
||||
"studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
|
||||
"migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts",
|
||||
"migration:run": "tsx -r dotenv/config migration.ts",
|
||||
"migration:up": "drizzle-kit up --config ./server/db/drizzle.config.ts",
|
||||
"migration:drop": "drizzle-kit drop --config ./server/db/drizzle.config.ts",
|
||||
"db:push": "drizzle-kit --config ./server/db/drizzle.config.ts",
|
||||
"db:truncate": "tsx -r dotenv/config ./server/db/reset.ts",
|
||||
"db:studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
|
||||
"lint": "biome lint",
|
||||
"db:seed": "tsx -r dotenv/config ./server/db/seed.ts",
|
||||
"db:clean": "tsx -r dotenv/config ./server/db/reset.ts",
|
||||
"docker:build": "./docker/build.sh",
|
||||
"docker:push": "./docker/push.sh",
|
||||
"docker:build:canary": "./docker/build.sh canary",
|
||||
"docker:push:canary": "./docker/push.sh canary",
|
||||
"version": "echo $(node -p \"require('./package.json').version\")",
|
||||
"test": "vitest --config __test__/vitest.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.515.0",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-yaml": "^6.1.1",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/legacy-modes": "6.4.0",
|
||||
"@dokploy/trpc-openapi": "0.0.4",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@lucia-auth/adapter-drizzle": "1.0.7",
|
||||
"@octokit/auth-app": "^6.0.4",
|
||||
"@octokit/webhooks": "^13.2.7",
|
||||
"@radix-ui/react-accordion": "1.1.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-radio-group": "^1.1.3",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-table": "^8.16.0",
|
||||
"@trpc/client": "^10.43.6",
|
||||
"@trpc/next": "^10.43.6",
|
||||
"@trpc/react-query": "^10.43.6",
|
||||
"@trpc/server": "^10.43.6",
|
||||
"@uiw/codemirror-theme-github": "^4.22.1",
|
||||
"@uiw/react-codemirror": "^4.22.1",
|
||||
"@xterm/addon-attach": "0.10.0",
|
||||
"@xterm/xterm": "^5.4.0",
|
||||
"bcrypt": "5.1.1",
|
||||
"bl": "6.0.11",
|
||||
"boxen": "^7.1.1",
|
||||
"bullmq": "5.4.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"cmdk": "^0.2.0",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"date-fns": "3.6.0",
|
||||
"dockerode": "4.0.2",
|
||||
"dockerode-compose": "^1.4.0",
|
||||
"dockerstats": "2.4.2",
|
||||
"dotenv": "16.4.5",
|
||||
"drizzle-orm": "^0.30.8",
|
||||
"drizzle-zod": "0.5.1",
|
||||
"hi-base32": "^0.5.1",
|
||||
"input-otp": "^1.2.4",
|
||||
"js-yaml": "4.1.0",
|
||||
"k6": "^0.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"lucia": "^3.0.1",
|
||||
"lucide-react": "^0.312.0",
|
||||
"nanoid": "3",
|
||||
"next": "^14.1.3",
|
||||
"next-themes": "^0.2.1",
|
||||
"node-os-utils": "1.3.7",
|
||||
"node-pty": "1.0.0",
|
||||
"node-schedule": "2.1.1",
|
||||
"octokit": "3.1.2",
|
||||
"otpauth": "^9.2.3",
|
||||
"postgres": "3.4.4",
|
||||
"public-ip": "6.0.2",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.49.3",
|
||||
"recharts": "^2.12.3",
|
||||
"slugify": "^1.6.6",
|
||||
"sonner": "^1.4.0",
|
||||
"superjson": "^2.2.1",
|
||||
"swagger-ui-react": "^5.17.14",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tar-fs": "3.0.5",
|
||||
"use-resize-observer": "9.1.0",
|
||||
"ws": "8.16.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
"zod": "^3.23.4",
|
||||
"nodemailer": "6.9.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.7.1",
|
||||
"@types/bcrypt": "5.0.2",
|
||||
"@types/dockerode": "3.3.23",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/lodash": "4.17.4",
|
||||
"@types/node": "^18.17.0",
|
||||
"@types/node-os-utils": "1.3.4",
|
||||
"@types/node-schedule": "2.1.6",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/swagger-ui-react": "^4.18.3",
|
||||
"@types/tar-fs": "2.0.4",
|
||||
"@types/ws": "8.5.10",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"drizzle-kit": "^0.21.1",
|
||||
"esbuild": "0.20.2",
|
||||
"localtunnel": "2.0.2",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.4.2",
|
||||
"vite-tsconfig-paths": "4.3.2",
|
||||
"vitest": "^1.6.0",
|
||||
"xterm-readline": "1.1.1",
|
||||
"@types/nodemailer": "^6.4.15"
|
||||
},
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.25.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0",
|
||||
"pnpm": ">=8.15.4"
|
||||
}
|
||||
}
|
||||
|
||||
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@@ -209,6 +209,9 @@ dependencies:
|
||||
node-schedule:
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
nodemailer:
|
||||
specifier: 6.9.14
|
||||
version: 6.9.14
|
||||
octokit:
|
||||
specifier: 3.1.2
|
||||
version: 3.1.2
|
||||
@@ -295,6 +298,9 @@ devDependencies:
|
||||
'@types/node-schedule':
|
||||
specifier: 2.1.6
|
||||
version: 2.1.6
|
||||
'@types/nodemailer':
|
||||
specifier: ^6.4.15
|
||||
version: 6.4.15
|
||||
'@types/qrcode':
|
||||
specifier: ^1.5.5
|
||||
version: 1.5.5
|
||||
@@ -5210,6 +5216,12 @@ packages:
|
||||
undici-types: 5.26.5
|
||||
dev: false
|
||||
|
||||
/@types/nodemailer@6.4.15:
|
||||
resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
|
||||
dependencies:
|
||||
'@types/node': 18.19.24
|
||||
dev: true
|
||||
|
||||
/@types/prop-types@15.7.11:
|
||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||
|
||||
@@ -8306,6 +8318,11 @@ packages:
|
||||
sorted-array-functions: 1.3.0
|
||||
dev: false
|
||||
|
||||
/nodemailer@6.9.14:
|
||||
resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dev: false
|
||||
|
||||
/nopt@5.0.0:
|
||||
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
@@ -5,15 +5,14 @@ import {
|
||||
} from "@/server/api/trpc";
|
||||
import { db } from "@/server/db";
|
||||
import {
|
||||
apiCreateDestination,
|
||||
apiCreateDiscord,
|
||||
apiCreateEmail,
|
||||
apiCreateSlack,
|
||||
apiCreateTelegram,
|
||||
apiFindOneNotification,
|
||||
apiSendTest,
|
||||
apiUpdateDestination,
|
||||
} from "@/server/db/schema";
|
||||
import { HeadBucketCommand, S3Client } from "@aws-sdk/client-s3";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { updateDestinationById } from "../services/destination";
|
||||
import {
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
findNotificationById,
|
||||
removeNotificationById,
|
||||
} from "../services/notification";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
export const notificationRouter = createTRPCRouter({
|
||||
createSlack: adminProcedure
|
||||
@@ -32,6 +32,7 @@ export const notificationRouter = createTRPCRouter({
|
||||
try {
|
||||
return await createSlackNotification(input);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to create the destination",
|
||||
@@ -97,30 +98,123 @@ export const notificationRouter = createTRPCRouter({
|
||||
return notification;
|
||||
}),
|
||||
testConnection: adminProcedure
|
||||
.input(apiCreateDestination)
|
||||
.input(apiSendTest)
|
||||
.mutation(async ({ input }) => {
|
||||
const { secretAccessKey, bucket, region, endpoint, accessKey } = input;
|
||||
const s3Client = new S3Client({
|
||||
region: region,
|
||||
...(endpoint && {
|
||||
endpoint: endpoint,
|
||||
}),
|
||||
credentials: {
|
||||
accessKeyId: accessKey,
|
||||
secretAccessKey: secretAccessKey,
|
||||
},
|
||||
forcePathStyle: true,
|
||||
});
|
||||
const headBucketCommand = new HeadBucketCommand({ Bucket: bucket });
|
||||
const notificationType = input.notificationType;
|
||||
console.log(input);
|
||||
|
||||
try {
|
||||
await s3Client.send(headBucketCommand);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error to connect to bucket",
|
||||
cause: error,
|
||||
});
|
||||
if (notificationType === "slack") {
|
||||
// go to your slack dashboard
|
||||
// go to integrations
|
||||
// add a new integration
|
||||
// select incoming webhook
|
||||
// copy the webhook url
|
||||
console.log("test slack");
|
||||
const { webhookUrl, channel } = input;
|
||||
try {
|
||||
const response = await fetch(webhookUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ text: "Test notification", channel }),
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
} else if (notificationType === "telegram") {
|
||||
// start telegram
|
||||
// search BotFather
|
||||
// send /newbot
|
||||
// name
|
||||
// name-with-bot-at-the-end
|
||||
// copy the token
|
||||
// search @userinfobot
|
||||
// send /start
|
||||
// copy the Id
|
||||
const { botToken, chatId } = input;
|
||||
try {
|
||||
const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
chat_id: chatId,
|
||||
text: "Test notification",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error sending Telegram notification: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Telegram notification sent successfully");
|
||||
} catch (error) {
|
||||
console.error("Error sending Telegram notification:", error);
|
||||
throw new Error("Error sending Telegram notification");
|
||||
}
|
||||
} else if (notificationType === "discord") {
|
||||
const { webhookUrl } = input;
|
||||
try {
|
||||
// go to your discord server
|
||||
// go to settings
|
||||
// go to integrations
|
||||
// add a new integration
|
||||
// select webhook
|
||||
// copy the webhook url
|
||||
const response = await fetch(webhookUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: "Test notification",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error sending Discord notification: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Discord notification sent successfully");
|
||||
} catch (error) {
|
||||
console.error("Error sending Discord notification:", error);
|
||||
throw new Error("Error sending Discord notification");
|
||||
}
|
||||
} else if (notificationType === "email") {
|
||||
const { smtpServer, smtpPort, username, password, toAddresses } = input;
|
||||
try {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: smtpServer,
|
||||
port: smtpPort,
|
||||
secure: smtpPort === "465",
|
||||
auth: {
|
||||
user: username,
|
||||
pass: password,
|
||||
},
|
||||
});
|
||||
// need to add a valid from address
|
||||
const fromAddress = "no-reply@emails.dokploy.com";
|
||||
const mailOptions = {
|
||||
from: fromAddress,
|
||||
to: toAddresses?.join(", "),
|
||||
subject: "Test email",
|
||||
text: "Test email",
|
||||
};
|
||||
|
||||
await transporter.sendMail(mailOptions);
|
||||
|
||||
console.log("Email notification sent successfully");
|
||||
} catch (error) {
|
||||
console.error("Error sending Email notification:", error);
|
||||
throw new Error("Error sending Email notification");
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ export const createSlackNotification = async (
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
notificationType: "slack",
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -90,6 +91,7 @@ export const createTelegramNotification = async (
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
notificationType: "telegram",
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -134,6 +136,7 @@ export const createDiscordNotification = async (
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
notificationType: "discord",
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -182,6 +185,7 @@ export const createEmailNotification = async (
|
||||
appBuildError: input.appBuildError,
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
notificationType: "email",
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -239,3 +243,22 @@ export const updateDestinationById = async (
|
||||
|
||||
return result[0];
|
||||
};
|
||||
|
||||
export const sendNotification = async (
|
||||
notificationData: Partial<Notification>,
|
||||
) => {
|
||||
// if(notificationData.notificationType === "slack"){
|
||||
// const { webhookUrl, channel } = notificationData;
|
||||
// try {
|
||||
// const response = await fetch(webhookUrl, {
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify({ text: "Test notification", channel }),
|
||||
// });
|
||||
// } catch (err) {
|
||||
// console.log(err);
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { generateAppName } from "./utils";
|
||||
import { registry } from "./registry";
|
||||
import { generatePassword } from "@/templates/utils";
|
||||
|
||||
export const sourceType = pgEnum("sourceType", ["docker", "git", "github"]);
|
||||
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
|
||||
export const notificationType = pgEnum("notificationType", [
|
||||
"slack",
|
||||
"telegram",
|
||||
"discord",
|
||||
"email",
|
||||
]);
|
||||
|
||||
export const notifications = pgTable("notification", {
|
||||
notificationId: text("notificationId")
|
||||
.notNull()
|
||||
@@ -15,6 +22,7 @@ export const notifications = pgTable("notification", {
|
||||
appBuildError: boolean("appBuildError").notNull().default(false),
|
||||
databaseBackup: boolean("databaseBackup").notNull().default(false),
|
||||
dokployRestart: boolean("dokployRestart").notNull().default(false),
|
||||
notificationType: notificationType("notificationType").notNull(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
@@ -67,6 +75,7 @@ export const email = pgTable("email", {
|
||||
smtpPort: text("smtpPort").notNull(),
|
||||
username: text("username").notNull(),
|
||||
password: text("password").notNull(),
|
||||
fromAddress: text("fromAddress").notNull(),
|
||||
toAddresses: text("toAddress").array().notNull(),
|
||||
});
|
||||
|
||||
@@ -149,6 +158,7 @@ export const apiCreateEmail = notificationsSchema
|
||||
smtpPort: z.string().min(1),
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
fromAddress: z.string().min(1),
|
||||
toAddresses: z.array(z.string()).min(1),
|
||||
})
|
||||
.required();
|
||||
@@ -158,3 +168,18 @@ export const apiFindOneNotification = notificationsSchema
|
||||
notificationId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiSendTest = notificationsSchema
|
||||
.extend({
|
||||
botToken: z.string(),
|
||||
chatId: z.string(),
|
||||
webhookUrl: z.string(),
|
||||
channel: z.string(),
|
||||
smtpServer: z.string(),
|
||||
smtpPort: z.string(),
|
||||
fromAddress: z.string(),
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
toAddresses: z.array(z.string()),
|
||||
})
|
||||
.partial();
|
||||
|
||||
Reference in New Issue
Block a user