feat(PROJ-17): Admin Dashboard Systemauslastung immer anzeigen
- Systemauslastungs-Sektion wird immer gerendert (nicht nur bei Erfolg) - Fehlermeldung wenn /api/admin/system/stats nicht erreichbar ist - Feature-Status auf In Review gesetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
imapv2 "github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/imapclient"
|
||||
)
|
||||
|
||||
// FolderInfo describes a single IMAP folder with exclusion metadata.
|
||||
type FolderInfo struct {
|
||||
Name string `json:"name"`
|
||||
Excluded bool `json:"excluded"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// junkTrashNames lists well-known junk/trash folder names for fallback detection.
|
||||
var junkTrashNames = []string{
|
||||
"junk", "spam", "trash", "deleted items",
|
||||
"deleted messages", "papierkorb", "gelöschte elemente",
|
||||
}
|
||||
|
||||
// Connect establishes an IMAP client connection using the specified TLS mode.
|
||||
func Connect(host string, port int, tlsMode string) (*imapclient.Client, error) {
|
||||
addr := fmt.Sprintf("%s:%d", host, port)
|
||||
|
||||
switch tlsMode {
|
||||
case "ssl":
|
||||
c, err := imapclient.DialTLS(addr, &imapclient.Options{
|
||||
TLSConfig: &tls.Config{ServerName: host},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imap connect ssl: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
case "starttls":
|
||||
c, err := imapclient.DialStartTLS(addr, &imapclient.Options{
|
||||
TLSConfig: &tls.Config{ServerName: host},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imap connect starttls: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
case "none":
|
||||
c, err := imapclient.DialInsecure(addr, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imap connect plain: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("imap: unknown tls mode %q", tlsMode)
|
||||
}
|
||||
}
|
||||
|
||||
// ListFolders retrieves all mailbox folders and detects junk/trash folders.
|
||||
func ListFolders(c *imapclient.Client) ([]FolderInfo, error) {
|
||||
listCmd := c.List("", "*", nil)
|
||||
mailboxes, err := listCmd.Collect()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imap list folders: %w", err)
|
||||
}
|
||||
|
||||
var folders []FolderInfo
|
||||
for _, mb := range mailboxes {
|
||||
fi := FolderInfo{Name: mb.Mailbox}
|
||||
|
||||
// Check special-use attributes (RFC 6154)
|
||||
for _, attr := range mb.Attrs {
|
||||
if attr == imapv2.MailboxAttrJunk {
|
||||
fi.Excluded = true
|
||||
fi.Reason = "special_use"
|
||||
break
|
||||
}
|
||||
if attr == imapv2.MailboxAttrTrash {
|
||||
fi.Excluded = true
|
||||
fi.Reason = "special_use"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: case-insensitive name matching
|
||||
if !fi.Excluded {
|
||||
lower := strings.ToLower(mb.Mailbox)
|
||||
for _, jt := range junkTrashNames {
|
||||
if lower == jt {
|
||||
fi.Excluded = true
|
||||
fi.Reason = "name_match"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
folders = append(folders, fi)
|
||||
}
|
||||
|
||||
return folders, nil
|
||||
}
|
||||
Reference in New Issue
Block a user