231 lines
6.8 KiB
TypeScript
231 lines
6.8 KiB
TypeScript
/**
|
|
* lib/mailcow.ts
|
|
* Mailcow API client — server-side only.
|
|
* Uses the single super-admin API key from .env.
|
|
*/
|
|
|
|
let BASE = process.env.MAILCOW_API_URL?.replace(/\/$/, "") ?? "";
|
|
if (BASE && !BASE.startsWith("http")) {
|
|
BASE = `https://${BASE}`;
|
|
}
|
|
const KEY = process.env.MAILCOW_API_KEY ?? "";
|
|
|
|
async function mfetch(path: string, options: RequestInit = {}) {
|
|
if (!BASE || !KEY) {
|
|
console.error("[Mailcow API] MAILCOW_API_URL or MAILCOW_API_KEY is not set in .env");
|
|
return new Response(JSON.stringify({ error: "Server configuration error" }), { status: 500 });
|
|
}
|
|
const url = `${BASE}/api/v1${path}`;
|
|
console.log(`[Mailcow API] ${options.method || "GET"} ${url}`);
|
|
try {
|
|
const res = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-API-Key": KEY,
|
|
...options.headers,
|
|
},
|
|
cache: "no-store",
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
console.error(`[Mailcow API] Error ${res.status}: ${text}`);
|
|
}
|
|
return res;
|
|
} catch (err: any) {
|
|
console.error(`[Mailcow API] Fetch failed: ${err.message}`);
|
|
return new Response(JSON.stringify({ error: "Connection to Mailcow failed" }), { status: 503 });
|
|
}
|
|
}
|
|
|
|
// ─── Types ─────────────────────────────────────────────────
|
|
|
|
export interface MailcowDomain {
|
|
domain_name: string;
|
|
description: string;
|
|
active: string; // "1" | "0"
|
|
mboxes_in_domain: number;
|
|
mboxes_left: number;
|
|
max_num_mboxes_for_domain: number;
|
|
quota_used_in_domain: string;
|
|
max_quota_for_domain: number;
|
|
max_quota_for_mbox: number;
|
|
aliases_in_domain: number;
|
|
aliases_left: number;
|
|
}
|
|
|
|
export interface MailcowMailbox {
|
|
username: string; // full email e.g. info@domain.com
|
|
name: string; // display name
|
|
local_part: string;
|
|
domain: string;
|
|
quota: number; // bytes
|
|
quota_used: number; // bytes
|
|
active: string; // "1" | "0"
|
|
created: string;
|
|
modified: string;
|
|
}
|
|
|
|
export interface MailcowDomainAdmin {
|
|
username: string;
|
|
active: string;
|
|
domains: string[];
|
|
created: string;
|
|
modified: string;
|
|
}
|
|
|
|
// ─── Domains ────────────────────────────────────────────────
|
|
|
|
export async function getDomains(): Promise<MailcowDomain[]> {
|
|
const res = await mfetch("/get/domain/all");
|
|
if (!res.ok) return [];
|
|
const data = await res.json();
|
|
return Array.isArray(data) ? data : [];
|
|
}
|
|
|
|
export async function getDomain(domain: string): Promise<MailcowDomain | null> {
|
|
const res = await mfetch(`/get/domain/${domain}`);
|
|
if (!res.ok) return null;
|
|
const data = await res.json();
|
|
return Array.isArray(data) ? data[0] ?? null : data ?? null;
|
|
}
|
|
|
|
export async function createDomain(payload: {
|
|
domain: string;
|
|
description?: string;
|
|
aliases?: number;
|
|
mailboxes?: number;
|
|
defquota?: number;
|
|
maxquota?: number;
|
|
quota?: number;
|
|
active?: number;
|
|
}) {
|
|
const body = {
|
|
active: 1,
|
|
aliases: 400,
|
|
mailboxes: 10,
|
|
defquota: Math.min(payload.quota || 10240, 3072),
|
|
maxquota: payload.quota || 10240,
|
|
quota: 10240,
|
|
...payload,
|
|
};
|
|
// Double check constraints
|
|
if (body.maxquota > body.quota) body.maxquota = body.quota;
|
|
if (body.defquota > body.maxquota) body.defquota = body.maxquota;
|
|
const res = await mfetch("/add/domain", { method: "POST", body: JSON.stringify(body) });
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
export async function deleteDomain(domain: string) {
|
|
const res = await mfetch("/delete/domain", {
|
|
method: "POST",
|
|
body: JSON.stringify([domain]),
|
|
});
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
// ─── Domain Admins ──────────────────────────────────────────
|
|
|
|
export async function getDomainAdmins(): Promise<MailcowDomainAdmin[]> {
|
|
const res = await mfetch("/get/domain-admin/all");
|
|
if (!res.ok) return [];
|
|
const data = await res.json();
|
|
return Array.isArray(data) ? data : Object.values(data);
|
|
}
|
|
|
|
export async function createDomainAdmin(payload: {
|
|
username: string;
|
|
password: string;
|
|
domains: string; // comma-separated or single domain
|
|
active?: number;
|
|
}) {
|
|
const body = {
|
|
active: 1,
|
|
password2: payload.password,
|
|
...payload,
|
|
};
|
|
const res = await mfetch("/add/domain-admin", { method: "POST", body: JSON.stringify(body) });
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
export async function deleteDomainAdmin(username: string) {
|
|
const res = await mfetch("/delete/domain-admin", {
|
|
method: "POST",
|
|
body: JSON.stringify([username]),
|
|
});
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
// ─── Mailboxes ──────────────────────────────────────────────
|
|
|
|
export async function getMailboxes(domain: string): Promise<MailcowMailbox[]> {
|
|
const res = await mfetch(`/get/mailbox/all/${domain}`);
|
|
if (!res.ok) return [];
|
|
const data = await res.json();
|
|
return Array.isArray(data) ? data : [];
|
|
}
|
|
|
|
export async function createMailbox(payload: {
|
|
local_part: string;
|
|
domain: string;
|
|
name: string;
|
|
password: string;
|
|
quota?: number;
|
|
active?: number;
|
|
}) {
|
|
const body = {
|
|
quota: 3072,
|
|
active: 1,
|
|
password2: payload.password,
|
|
...payload,
|
|
};
|
|
const res = await mfetch("/add/mailbox", { method: "POST", body: JSON.stringify(body) });
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
export async function deleteMailbox(emails: string[]) {
|
|
const res = await mfetch("/delete/mailbox", {
|
|
method: "POST",
|
|
body: JSON.stringify(emails),
|
|
});
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
export async function editMailbox(
|
|
emails: string[],
|
|
attr: {
|
|
password?: string;
|
|
password2?: string;
|
|
active?: number;
|
|
quota?: number;
|
|
name?: string;
|
|
}
|
|
) {
|
|
const body = { items: emails, attr };
|
|
const res = await mfetch("/edit/mailbox", { method: "POST", body: JSON.stringify(body) });
|
|
const data = await res.json();
|
|
return { ok: res.ok, data };
|
|
}
|
|
|
|
// ─── Status ─────────────────────────────────────────────────
|
|
|
|
export async function getVersionStatus() {
|
|
const res = await mfetch("/get/status/version");
|
|
if (!res.ok) return null;
|
|
return res.json();
|
|
}
|
|
|
|
// ─── DKIM ────────────────────────────────────────────────────
|
|
|
|
export async function getDKIM(domain: string) {
|
|
const res = await mfetch(`/get/dkim/${domain}`);
|
|
if (!res.ok) return null;
|
|
return res.json();
|
|
}
|