feat: list logged-in accounts, storage quota and auth method in account settings

This commit is contained in:
Linus Rath
2026-08-29 18:11:13 +02:00
parent 1f2df5f079
commit 490355c65e
3 changed files with 330 additions and 78 deletions
+55
View File
@@ -0,0 +1,55 @@
import { jmapClient } from './jmap-client';
import { CAPABILITIES } from './types';
// RFC 9425 Quota object (Stalwart reports storage as resourceType "octets"
// with scope "account"); older servers used a pre-RFC "mail" shape.
interface JMAPQuota {
id: string;
resourceType?: string;
used?: number;
hardLimit?: number;
warnLimit?: number;
softLimit?: number;
// pre-RFC shape
limit?: number;
scope?: string;
types?: string[];
name?: string;
}
export interface MailQuota {
used: number;
total: number;
}
export function serverSupportsQuota(): boolean {
return Boolean(jmapClient.currentSession?.capabilities?.[CAPABILITIES.QUOTA]);
}
/**
* Storage usage for the primary account, or null when the server does not
* advertise urn:ietf:params:jmap:quota or reports no mail storage quota.
* Mirrors the webmail's client.getQuota().
*/
export async function fetchMailQuota(): Promise<MailQuota | null> {
if (!serverSupportsQuota()) return null;
try {
const res = await jmapClient.request(
[['Quota/get', { accountId: jmapClient.accountId }, '0']],
[CAPABILITIES.CORE, CAPABILITIES.QUOTA],
);
const [name, body] = res.methodResponses[0] ?? [];
if (name !== 'Quota/get') return null;
const quotas = (body?.list ?? []) as JMAPQuota[];
const coversMail = (q: JMAPQuota) =>
!q.types?.length || q.types.some((t) => t === 'Email' || t === 'Mail');
const mailQuota =
quotas.find((q) => q.resourceType === 'octets' && coversMail(q))
|| quotas.find((q) => q.resourceType === 'mail' || q.scope === 'mail');
if (!mailQuota) return null;
const total = mailQuota.hardLimit ?? mailQuota.limit ?? 0;
return { used: mailQuota.used ?? 0, total };
} catch {
return null;
}
}