Implement database migration, notification logs, and one-click Mailcow setup

This commit is contained in:
AyrisAI
2026-05-14 16:49:11 +03:00
parent f328296c64
commit b024e20027
18 changed files with 1067 additions and 166 deletions

View File

@@ -19,6 +19,13 @@ interface Domain {
domain_name: string;
}
interface User {
id: string;
name: string;
email: string;
telegramId?: string;
}
export default function MailboxesPage() {
const { data: session } = useSession();
const [domains, setDomains] = useState<Domain[]>([]);
@@ -30,12 +37,14 @@ export default function MailboxesPage() {
const [showInfoModal, setShowInfoModal] = useState<string | null>(null);
const [isPending, startTransition] = useTransition();
const [search, setSearch] = useState("");
const [createForm, setCreateForm] = useState({ local_part: "", name: "", password: "", quota: 3072 });
const [users, setUsers] = useState<User[]>([]);
const [createForm, setCreateForm] = useState({ local_part: "", name: "", password: "", quota: 3072, notifyUserId: "" });
const [newPassword, setNewPassword] = useState("");
const [formError, setFormError] = useState("");
const dict = useDictionary();
useEffect(() => {
// Fetch domains
fetch("/api/domains")
.then((r) => r.json())
.then((data: Domain[]) => {
@@ -44,6 +53,13 @@ export default function MailboxesPage() {
setSelectedDomain(data[0].domain_name);
}
});
// Fetch users for mapping selection
fetch("/api/users")
.then((r) => r.json())
.then((data) => {
if (Array.isArray(data)) setUsers(data);
});
}, []);
const fetchMailboxes = useCallback(async (domain: string) => {
@@ -63,21 +79,46 @@ export default function MailboxesPage() {
e.preventDefault();
setFormError("");
startTransition(async () => {
const res = await fetch("/api/mailboxes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...createForm, domain: selectedDomain }),
});
const data = await res.json();
if (res.ok) {
setShowCreateModal(false);
setCreateForm({ local_part: "", name: "", password: "", quota: 3072 });
fetchMailboxes(selectedDomain);
} else {
const msg = Array.isArray(data)
? data.map((d: { msg?: unknown }) => JSON.stringify(d.msg)).join(", ")
: (data?.error ?? "Mailcow bağlantısını kontrol edin");
setFormError(String(msg));
try {
const res = await fetch("/api/mailboxes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...createForm, domain: selectedDomain }),
});
let data: any = {};
try {
data = await res.json();
} catch (e) {
data = { error: "Sunucudan geçersiz yanıt geldi (JSON hatası)." };
}
if (res.ok) {
// If a notification user is selected, create the mapping
if (createForm.notifyUserId) {
const fullEmail = `${createForm.local_part}@${selectedDomain}`;
await fetch("/api/mappings", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: fullEmail,
userId: createForm.notifyUserId,
}),
});
}
setShowCreateModal(false);
setCreateForm({ local_part: "", name: "", password: "", quota: 3072, notifyUserId: "" });
fetchMailboxes(selectedDomain);
} else {
const msg = Array.isArray(data)
? data.map((d: { msg?: unknown }) => JSON.stringify(d.msg)).join(", ")
: (data?.error ?? "Mailcow bağlantısını veya veritabanını kontrol edin");
setFormError(String(msg));
}
} catch (error: any) {
console.error("Mailbox creation failed:", error);
setFormError(error.message);
}
});
};
@@ -333,6 +374,21 @@ export default function MailboxesPage() {
<input type="number" className="input" value={createForm.quota} min={100} max={102400}
onChange={(e) => setCreateForm({ ...createForm, quota: parseInt(e.target.value) || 3072 })} />
</div>
<div>
<label className="label">{dict.mailboxes.notifyUser || "Bildirim Gidecek Kullanıcı (TG)"}</label>
<select
className="input"
value={createForm.notifyUserId}
onChange={(e) => setCreateForm({ ...createForm, notifyUserId: e.target.value })}
>
<option value="">-- {dict.mailboxes.noNotify || "Bildirim Gönderme"} --</option>
{users.map((u) => (
<option key={u.id} value={u.id}>
{u.name || u.email} {u.telegramId ? `(TG: ${u.telegramId})` : ""}
</option>
))}
</select>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-ghost" onClick={() => setShowCreateModal(false)}>{dict.mailboxes.cancel || "İptal"}</button>