feat(webmail,auth): leitor estilo Outlook, responder a todos, importantes e isolamento do admin de dominio
Webmail - Leitor de mensagem em componente proprio, com barra de comandos fixa, cabecalho compacto, faixa de imagens bloqueadas e chips de anexo. - Divisor arrastavel entre lista e leitor (mouse, setas, duplo clique), largura persistida e limites que preservam os dois paineis. - Destinatarios em chips (Para, Cc, Cco): aceita nomes com virgula, colagem de listas, validacao e bloqueio de envio com endereco invalido. - Responder a todos, sem o proprio endereco e sem repetidos; In-Reply-To e References nas respostas; prefixo Re:/Fwd: sem repeticao. - Marcar como importante (estrela, filtro, linha da lista) e enviar com alta importancia (Importance/X-Priority), com etiqueta no leitor. - Sanitizador: a regexp de URI customizada descartava todo atributo que nao fosse link (width, colspan, bgcolor...), o que desmontava tabelas de e-mail; imagens de fundo agora sao bloqueadas de forma reversivel. Protocols - POST /api/v1/mail/flagged (\Flagged) com bump de modseq para IMAP. - Renderizacao expoe message_id, in_reply_to e important. - Cabecalhos de resposta validados contra injecao. Auth - As views do admin de dominio eram globais: dashboard, fila de saida, DMARC, preflight e diagnostico mostravam dados de todos os dominios. Agora todas respeitam o escopo da sessao; super admin segue vendo tudo. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b5baf33a63
commit
50a67531ba
30 files changed
+1810
-348
No files matched your search
@@ -34,6 +34,15 @@ type RenderedMessage struct {
|
||||
Text string `json:"text"`
|
||||
HTML string `json:"html"`
|
||||
Attachments []string `json:"attachments"`
|
||||
// MessageID is the Message-ID header, so a reply can thread under this
|
||||
// message. Empty when the message has none.
|
||||
MessageID string `json:"message_id"`
|
||||
// InReplyTo is the Message-ID this message answers, so reopening a draft
|
||||
// keeps the reply threaded.
|
||||
InReplyTo string `json:"in_reply_to"`
|
||||
// Important is true when the sender asked for priority: Importance: high,
|
||||
// or X-Priority 1 or 2.
|
||||
Important bool `json:"important"`
|
||||
// InlineImages maps a part's Content-ID - without the angle brackets, the
|
||||
// way the HTML body spells it in src="cid:..." - to a self-contained
|
||||
// data: URI.
|
||||
@@ -79,6 +88,9 @@ func RenderMessage(raw []byte, uid uint32) RenderedMessage {
|
||||
}
|
||||
}
|
||||
}
|
||||
out.MessageID = strings.TrimSpace(header.Get("Message-Id"))
|
||||
out.InReplyTo = strings.TrimSpace(header.Get("In-Reply-To"))
|
||||
out.Important = isHighPriority(header.Get("Importance"), header.Get("X-Priority"))
|
||||
// A missing Date header parses without error into the zero time, which
|
||||
// would render as the year 0001 in the reader. Left empty, the UI falls
|
||||
// back to the received timestamp it already has.
|
||||
@@ -214,3 +226,14 @@ func ExtractAttachment(entity *message.Entity, targetName string) ([]byte, strin
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// isHighPriority reads the two headers clients use to mark urgent mail.
|
||||
// X-Priority is "1 (Highest)" or "2 (High)"; anything else, or nothing, is
|
||||
// ordinary.
|
||||
func isHighPriority(importance, xPriority string) bool {
|
||||
if strings.EqualFold(strings.TrimSpace(importance), "high") {
|
||||
return true
|
||||
}
|
||||
fields := strings.Fields(xPriority)
|
||||
return len(fields) > 0 && (fields[0] == "1" || fields[0] == "2")
|
||||
}
|
||||
Reference in new issue
Block a user