feat: complete i18n support, telegram webhook, and security improvements
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState, useEffect, useTransition } from "react";
|
||||
import { formatBytes } from "@/lib/format";
|
||||
import { useDictionary } from "@/components/DictionaryContext";
|
||||
|
||||
interface Domain {
|
||||
domain_name: string;
|
||||
@@ -23,6 +24,7 @@ export default function DomainsPage() {
|
||||
const [search, setSearch] = useState("");
|
||||
const [form, setForm] = useState({ domain: "", description: "", mailboxes: "10", quota: "10240", maxquota: "10240" });
|
||||
const [formError, setFormError] = useState("");
|
||||
const dict = useDictionary();
|
||||
|
||||
const fetchDomains = async () => {
|
||||
setLoading(true);
|
||||
@@ -54,14 +56,23 @@ export default function DomainsPage() {
|
||||
fetchDomains();
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const msg = Array.isArray(data) ? data.map((d: { msg?: string }) => d.msg).join(", ") : (data?.error ?? "Bir hata oluştu");
|
||||
setFormError(String(msg));
|
||||
let msg = "Bir hata oluştu";
|
||||
if (Array.isArray(data)) {
|
||||
msg = data.map((d: any) => {
|
||||
if (typeof d.msg === "string") return d.msg;
|
||||
if (Array.isArray(d.msg)) return d.msg.join(", ");
|
||||
return JSON.stringify(d.msg || d);
|
||||
}).join(" | ");
|
||||
} else if (data?.error) {
|
||||
msg = data.error;
|
||||
}
|
||||
setFormError(msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (domain: string) => {
|
||||
if (!confirm(`"${domain}" domainini Mailcow'dan silmek istediğinizden emin misiniz?\n\nBu işlem geri alınamaz!`)) return;
|
||||
if (!confirm(`"${domain}" domainini silmek istediğinizden emin misiniz?\n\nBu işlem geri alınamaz!`)) return;
|
||||
startTransition(async () => {
|
||||
await fetch(`/api/domains/${encodeURIComponent(domain)}`, { method: "DELETE" });
|
||||
fetchDomains();
|
||||
@@ -78,11 +89,11 @@ export default function DomainsPage() {
|
||||
<>
|
||||
<div className="page-header">
|
||||
<div>
|
||||
<h1 className="page-title">Domainler</h1>
|
||||
<p className="page-subtitle">Mailcow üzerindeki tüm domainleri yönetin</p>
|
||||
<h1 className="page-title">{dict.domains.title || "Domainler"}</h1>
|
||||
<p className="page-subtitle">{domains.length} {dict.domains.subtitle || "domain listeleniyor"}</p>
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={() => setShowModal(true)}>
|
||||
<PlusIcon /> Domain Ekle
|
||||
<PlusIcon /> {dict.domains.addDomain || "Domain Ekle"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -93,13 +104,13 @@ export default function DomainsPage() {
|
||||
<input
|
||||
type="text"
|
||||
className="input search-input"
|
||||
placeholder="Domain veya açıklama ara..."
|
||||
placeholder={dict.domains.searchPlaceholder || "Domain ara..."}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button className="btn btn-ghost" onClick={fetchDomains}>
|
||||
<RefreshIcon /> Yenile
|
||||
<RefreshIcon /> {dict.domains.refresh || "Yenile"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -109,19 +120,19 @@ export default function DomainsPage() {
|
||||
) : filtered.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<div className="empty-icon"><GlobeIcon size={24} /></div>
|
||||
<div style={{ fontWeight: 600 }}>Domain bulunamadı</div>
|
||||
<div style={{ fontSize: 12 }}>Mailcow'a domain eklemek için "Domain Ekle" butonuna tıklayın.</div>
|
||||
<div style={{ fontWeight: 600 }}>{dict.domains.noDomains || "Domain bulunamadı"}</div>
|
||||
<div style={{ fontSize: 12 }}>{dict.domains.tryDiffSearch || "Farklı bir arama yapın."}</div>
|
||||
</div>
|
||||
) : (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Mail Kutuları</th>
|
||||
<th>Alias</th>
|
||||
<th>Kota</th>
|
||||
<th>Durum</th>
|
||||
<th>İşlemler</th>
|
||||
<th>{dict.domains.domain || "Domain"}</th>
|
||||
<th>{dict.domains.mailboxes || "Mail Kutuları"}</th>
|
||||
<th>{dict.domains.aliases || "Alias"}</th>
|
||||
<th>{dict.domains.quota || "Kota"}</th>
|
||||
<th>{dict.domains.status || "Durum"}</th>
|
||||
<th>{dict.domains.actions || "İşlemler"}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -160,7 +171,7 @@ export default function DomainsPage() {
|
||||
</td>
|
||||
<td>
|
||||
<span className={`badge ${String(d.active) === "1" ? "badge-green" : "badge-red"}`}>
|
||||
{String(d.active) === "1" ? "● Aktif" : "● Pasif"}
|
||||
{String(d.active) === "1" ? `● ${dict.domains.active || "Aktif"}` : `● ${dict.domains.inactive || "Pasif"}`}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@@ -181,20 +192,20 @@ export default function DomainsPage() {
|
||||
<div className="modal-overlay" onClick={(e) => e.target === e.currentTarget && setShowModal(false)}>
|
||||
<div className="modal">
|
||||
<div className="modal-header">
|
||||
<h2 className="modal-title">Mailcow'a Domain Ekle</h2>
|
||||
<h2 className="modal-title">{dict.domains.addDomain || "Domain Ekle"}</h2>
|
||||
<button className="modal-close" onClick={() => setShowModal(false)}><XIcon /></button>
|
||||
</div>
|
||||
<form onSubmit={handleCreate}>
|
||||
<div className="modal-body form-group">
|
||||
{formError && <div className="error-msg">{formError}</div>}
|
||||
<div>
|
||||
<label className="label">Domain Adı</label>
|
||||
<label className="label">{dict.domains.domain || "Domain Adı"}</label>
|
||||
<input type="text" className="input" placeholder="ornek.com" value={form.domain}
|
||||
onChange={(e) => setForm({ ...form, domain: e.target.value })} required />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Açıklama (isteğe bağlı)</label>
|
||||
<input type="text" className="input" placeholder="Bu domain hakkında..." value={form.description}
|
||||
<label className="label">{dict.domains.description || "Açıklama"}</label>
|
||||
<input type="text" className="input" placeholder="" value={form.description}
|
||||
onChange={(e) => setForm({ ...form, description: e.target.value })} />
|
||||
</div>
|
||||
<div className="form-row">
|
||||
|
||||
Reference in New Issue
Block a user