Files
moemail/app/components/profile/webhook-config.tsx

208 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* eslint-disable @typescript-eslint/no-unused-vars */
"use client"
import { useState, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { useToast } from "@/components/ui/use-toast"
import { Loader2, Send, ChevronDown, ChevronUp } from "lucide-react"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
export function WebhookConfig() {
const [enabled, setEnabled] = useState(false)
const [url, setUrl] = useState("")
const [loading, setLoading] = useState(false)
const [testing, setTesting] = useState(false)
const [showDocs, setShowDocs] = useState(false)
const [initialLoading, setInitialLoading] = useState(true)
const { toast } = useToast()
useEffect(() => {
fetch("/api/webhook")
.then(res => res.json() as Promise<{ enabled: boolean; url: string }>)
.then(data => {
setEnabled(data.enabled)
setUrl(data.url)
})
.catch(console.error)
.finally(() => setInitialLoading(false))
}, [])
if (initialLoading) {
return (
<div className="text-center">
<div className="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center mx-auto">
<Loader2 className="w-6 h-6 text-primary animate-spin" />
</div>
<div>
<p className="text-sm text-muted-foreground">...</p>
</div>
</div>
)
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!url) return
setLoading(true)
try {
const res = await fetch("/api/webhook", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url, enabled })
})
if (!res.ok) throw new Error("Failed to save")
toast({
title: "保存成功",
description: "Webhook 配置已更新"
})
} catch (_error) {
toast({
title: "保存失败",
description: "请稍后重试",
variant: "destructive"
})
} finally {
setLoading(false)
}
}
const handleTest = async () => {
if (!url) return
setTesting(true)
try {
const res = await fetch("/api/webhook/test", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url })
})
if (!res.ok) throw new Error("测试失败")
toast({
title: "测试成功",
description: "Webhook 调用成功,请检查目标服务器是否收到请求"
})
} catch (_error) {
toast({
title: "测试失败",
description: "请检查 URL 是否正确且可访问",
variant: "destructive"
})
} finally {
setTesting(false)
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label> Webhook</Label>
<div className="text-sm text-muted-foreground">
URL
</div>
</div>
<Switch
checked={enabled}
onCheckedChange={setEnabled}
/>
</div>
{enabled && (
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="webhook-url">Webhook URL</Label>
<div className="flex gap-2">
<Input
id="webhook-url"
placeholder="https://example.com/webhook"
value={url}
onChange={(e) => setUrl(e.target.value)}
type="url"
required
/>
<Button type="submit" disabled={loading} className="flex-shrink-0">
{loading ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
"保存"
)}
</Button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="outline"
onClick={handleTest}
disabled={testing || !url}
>
{testing ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Send className="w-4 h-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>
<p> Webhook</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<p className="text-xs text-muted-foreground">
URL POST ,
</p>
</div>
<div className="space-y-2">
<button
type="button"
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
onClick={() => setShowDocs(!showDocs)}
>
{showDocs ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}
</button>
{showDocs && (
<div className="rounded-md bg-muted p-4 text-sm space-y-3">
<p> URL POST :</p>
<pre className="bg-background p-2 rounded text-xs">
Content-Type: application/json{'\n'}
X-Webhook-Event: new_message
</pre>
<p>:</p>
<pre className="bg-background p-2 rounded text-xs overflow-auto">
{`{
"emailId": "email-uuid",
"messageId": "message-uuid",
"fromAddress": "sender@example.com",
"subject": "邮件主题",
"content": "邮件文本内容",
"html": "邮件HTML内容",
"receivedAt": "2024-01-01T12:00:00.000Z",
"toAddress": "your-email@${window.location.host}"
}`}
</pre>
</div>
)}
</div>
</div>
)}
</form>
)
}