메일 서비스(사이트 대표·독립)·메일함·사용량·보안·전환·보존과 AriMail 도입 수정(KST 월 경계, 발신 키 오류, 도메인별 보장량, 100% 제한, 발송 큐·속도, 운영 상태, SSO) (§15~§18·§0.4)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
a014e3cd05
commit
a1cde81332
@@ -24,7 +24,7 @@
|
||||
* limits : { system:{…}, admin:{…} } — 설치 시 시스템 제한과 관리자 제한(안내용)
|
||||
*/
|
||||
import { json, bad, safeEqual, now } from './util.mjs';
|
||||
import { systemLimits, readAdminLimits, mailboxUsage, mailboxQuota, totalUsage, accountCount, statsTimezone, periodBounds, countMail, percentOf } from './limits.mjs';
|
||||
import { systemLimits, readAdminLimits, mailboxUsage, mailboxQuota, totalUsage, accountCount, statsTimezone, periodBounds, countMail, percentOf, limitState, statsNow } from './limits.mjs';
|
||||
import { listUsers, externalUsers, authMode, audit, mailboxOwner, standalone } from './auth.mjs';
|
||||
import { largeFileStats } from './files.mjs';
|
||||
import { inboundMode, outboundMode } from './providers/index.mjs';
|
||||
@@ -128,7 +128,7 @@ const dateKey = (iso, tz) => { try { return new Intl.DateTimeFormat('en-CA', { t
|
||||
*/
|
||||
async function dailySeries(env, days, tz) {
|
||||
// 시작 = (오늘 0시 - (days-1)일) 이 속한 날의 0시. 24시간 단위로 빼기만 하면 서머타임 경계에서 한 시간이 밀리므로 그 날의 정오를 잡아 다시 0시를 구합니다.
|
||||
const today = periodBounds('day', tz); const approx = Date.parse(today.start) - (days - 1) * DAY_MS;
|
||||
const today = periodBounds('day', tz, statsNow(env)); const approx = Date.parse(today.start) - (days - 1) * DAY_MS;
|
||||
const start = periodBounds('day', tz, new Date(approx + DAY_MS / 2)).start, startMs = Date.parse(start), end = today.end;
|
||||
const rows = (await env.DB.prepare(`SELECT direction, mailbox, raw_size, sent_at, received_at FROM messages WHERE (direction='inbound' AND received_at>=? AND received_at<?) OR (direction='outbound' AND state='sent' AND sent_at>=? AND sent_at<?)`).bind(start, end, start, end).all()).results;
|
||||
const byDate = new Map(); for (let i = 0; i < days; i++) { const d = dateKey(new Date(startMs + i * DAY_MS + DAY_MS / 2).toISOString(), tz); byDate.set(d, { date: d, sent: 0, received: 0, sentBytes: 0, receivedBytes: 0 }); }
|
||||
@@ -156,15 +156,18 @@ export async function domainUsage(env, { days = 30, accounts = true, series = tr
|
||||
const tz = statsTimezone(env), sys = systemLimits(env), admin = await readAdminLimits(env);
|
||||
const counts = await env.DB.prepare("SELECT COUNT(*) total, SUM(CASE WHEN direction='inbound' THEN 1 ELSE 0 END) inbound, SUM(CASE WHEN direction='outbound' AND state='sent' THEN 1 ELSE 0 END) outbound, SUM(CASE WHEN state IN ('draft','sending') THEN 1 ELSE 0 END) drafts, SUM(CASE WHEN state='failed' THEN 1 ELSE 0 END) failed FROM messages").first();
|
||||
const total = await totalUsage(env), local = await accountCount(env), ext = await externalUsers(env);
|
||||
const traffic = {}; for (const kind of ['day', 'week', 'month']) traffic[kind] = await periodTraffic(env, periodBounds(kind, tz));
|
||||
const traffic = {}; for (const kind of ['day', 'week', 'month']) traffic[kind] = await periodTraffic(env, periodBounds(kind, tz, statsNow(env)));
|
||||
const limits = await limitState(env); // [Ariweb Suite 변경] 경고 구간·제한 사유
|
||||
const out = {
|
||||
domain: mailDomain(env), primary: !env.TENANTS || env.TENANTS.primary.env === env, enabled: entry ? entry.enabled !== false : true, hosts: entry?.hosts || [env.PUBLIC_HOST].filter(Boolean),
|
||||
authMode: authMode(env), mailbox: String(env.MAILBOX || ''), inbound: inboundMode(env), outbound: outboundMode(env), timezone: tz, generatedAt: now(),
|
||||
accounts: { local, external: ext.accounts.length, total: local + ext.accounts.length, limit: sys.accounts, percent: percentOf(local, sys.accounts), externalSource: ext.accounts.length || authMode(env) === 'webmaker' ? { kind: authMode(env), ok: ext.ok !== false, error: ext.error || null } : null },
|
||||
storage: { bytes: total, limit: sys.totalBytes, percent: percentOf(total, sys.totalBytes), largeFiles: await largeFileStats(env) },
|
||||
messages: { total: Number(counts?.total || 0), inbound: Number(counts?.inbound || 0), outbound: Number(counts?.outbound || 0), drafts: Number(counts?.drafts || 0), failed: Number(counts?.failed || 0) },
|
||||
traffic: { ...traffic, monthlyLimit: sys.monthlyMessages, monthlyPercent: percentOf(traffic.month.total, sys.monthlyMessages) },
|
||||
traffic: { ...traffic, monthlyLimit: sys.monthlyMessages, monthlyPercent: percentOf(traffic.month.total, sys.monthlyMessages), countingRule: 'per_final_recipient', monthReset: limits.monthReset },
|
||||
limits: { system: sys, admin },
|
||||
// [Ariweb Suite 변경] 포털 표시용: 사이트 대표/독립 구분, 운영 상태, 경고 구간(0/70/80/90/95/100), 제한 사유.
|
||||
ariweb: entry?.ariweb || env.TENANT?.ariweb || null, operating: limits.operating, warnLevel: limits.warnLevel, warnLevels: limits.levels, limitedReasons: limits.limitedReasons, measuredAt: limits.measuredAt,
|
||||
};
|
||||
if (series || accounts) {
|
||||
const d = await dailySeries(env, days, tz);
|
||||
|
||||
Reference in New Issue
Block a user