feat(web): 多语言界面(中/英/日)与用户语言偏好字段
- 新增 internal/i18n 包:以原始中文字符串为 key 的翻译目录(zh 恒等, en 兜底,ja 缺译回退 en),t/tf 模板函数与 Accept-Language 解析 (按 q 值排序,兜底英语) - User 模型新增 language 字段(auto|en|zh|ja,默认 auto), AutoMigrate 自动加列;UserStore 新增 UpdateLanguage - 语言解析:登录用户按偏好(auto 时取浏览器 Accept-Language), 登录/封禁页按浏览器语言;所有 c.HTML 渲染注入 Lang 模板数据 - 全部页面模板(用户页 + 管理后台 12 页)接入翻译:导航、文件夹、 邮件列表/阅读/写信、设置、登录、封禁页、表单、表格、JS 确认框等; html lang 属性随语言切换;日期函数(shortDate/time12/time12m) 与 folderLabel 改为语言感知(上午/下午 vs AM/PM vs 午前/午後) - 设置页新增「界面语言」选择(自动/English/中文/日本語), /settings 表单支持单独提交语言偏好;管理后台用户表单可预设语言 - 封禁原因等入库中文文案在展示时经 t 翻译(未知 key 原样回退) - 测试:i18n 单测(解析/回退/译文)、render 测试补 Lang 数据、 时间格式化测试适配新签名;全量 go test 通过
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
{{define "admin_bans"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>IP黑名单 - MailGo</title>
|
||||
<title>{{t .Lang "IP黑名单"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,32 +12,32 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
|
||||
<h2>IP 黑名单</h2>
|
||||
<span style="color:#7f8c8d;font-size:13px;">阶段性封禁:第 4 次触发起封禁,30分钟 → 3小时 → 3个月 → 半年(上限)</span>
|
||||
<h2>{{t .Lang "IP 黑名单"}}</h2>
|
||||
<span style="color:#7f8c8d;font-size:13px;">{{t .Lang "阶段性封禁:第 4 次触发起封禁,30分钟 → 3小时 → 3个月 → 半年(上限)"}}</span>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP 地址</th>
|
||||
<th>状态</th>
|
||||
<th>封禁次数</th>
|
||||
<th>失败次数</th>
|
||||
<th>原因</th>
|
||||
<th>到期时间</th>
|
||||
<th>操作</th>
|
||||
<th>{{t .Lang "IP 地址"}}</th>
|
||||
<th>{{t .Lang "状态"}}</th>
|
||||
<th>{{t .Lang "封禁次数"}}</th>
|
||||
<th>{{t .Lang "失败次数"}}</th>
|
||||
<th>{{t .Lang "原因"}}</th>
|
||||
<th>{{t .Lang "到期时间"}}</th>
|
||||
<th>{{t .Lang "操作"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -45,21 +45,21 @@
|
||||
<tr>
|
||||
<td>{{.IPAddress}}</td>
|
||||
<td>
|
||||
{{if .Active}}<span class="badge" style="background:#e74c3c;color:#fff;">封禁中</span>
|
||||
{{else}}<span class="badge" style="background:#95a5a6;color:#fff;">已过期</span>{{end}}
|
||||
{{if .Active}}<span class="badge" style="background:#e74c3c;color:#fff;">{{t $.Lang "封禁中"}}</span>
|
||||
{{else}}<span class="badge" style="background:#95a5a6;color:#fff;">{{t $.Lang "已过期"}}</span>{{end}}
|
||||
</td>
|
||||
<td>
|
||||
{{if .BanCount}}
|
||||
{{if gt .BanCount 3}}第{{sub .BanCount 3}}次封禁{{else}}仅计数{{end}}
|
||||
{{if gt .BanCount 3}}{{tf $.Lang "第 %d 次封禁" (sub .BanCount 3)}}{{else}}{{t $.Lang "仅计数"}}{{end}}
|
||||
{{else}}—{{end}}
|
||||
</td>
|
||||
<td>{{.FailCount}}</td>
|
||||
<td>{{if .Reason}}{{.Reason}}{{else}}—{{end}}</td>
|
||||
<td>{{time12 .ExpiresAt}}{{if not .Active}}(已过期){{end}}</td>
|
||||
<td>{{if .Reason}}{{t $.Lang .Reason}}{{else}}—{{end}}</td>
|
||||
<td>{{time12 $.Lang .ExpiresAt}}{{if not .Active}}{{t $.Lang "(已过期)"}}{{end}}</td>
|
||||
<td>
|
||||
<form method="POST" action="/admin/bans/{{.ID}}/unban" style="display:inline;"
|
||||
onsubmit="return confirm('确定要解封 IP {{.IPAddress}} 吗?解封后该 IP 的封禁档位将清零。');">
|
||||
<button type="submit" class="btn btn-primary btn-sm">解封</button>
|
||||
onsubmit="return confirm('{{tf $.Lang "确定要解封 IP %s 吗?解封后该 IP 的封禁档位将清零。" .IPAddress}}');">
|
||||
<button type="submit" class="btn btn-primary btn-sm">{{t $.Lang "解封"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -67,17 +67,17 @@
|
||||
</tbody>
|
||||
</table>
|
||||
{{if not .rows}}
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">暂无封禁记录</p>
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">{{t $.Lang "暂无封禁记录"}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .totalPages}}
|
||||
<div class="pagination">
|
||||
{{if gt .page 1}}
|
||||
<a href="/admin/bans?page={{sub .page 1}}">上一页</a>
|
||||
<a href="/admin/bans?page={{sub .page 1}}">{{t $.Lang "上一页"}}</a>
|
||||
{{end}}
|
||||
<span>第 {{.page}} / {{.totalPages}} 页(共 {{.total}} 条)</span>
|
||||
<span>{{tf $.Lang "第 %d / %d 页(共 %d 条)" .page .totalPages .total}}</span>
|
||||
{{if lt .page .totalPages}}
|
||||
<a href="/admin/bans?page={{add .page 1}}">下一页</a>
|
||||
<a href="/admin/bans?page={{add .page 1}}">{{t $.Lang "下一页"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_connections"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>当前连接 - MailGo</title>
|
||||
<title>{{t .Lang "当前连接"}} - MailGo</title>
|
||||
<meta http-equiv="refresh" content="5">
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
@@ -13,26 +13,26 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
|
||||
<h2>当前连接(SMTP / IMAP / POP3)</h2>
|
||||
<span style="color:#7f8c8d;font-size:13px;">每 5 秒自动刷新</span>
|
||||
<h2>{{t .Lang "当前连接(SMTP / IMAP / POP3)"}}</h2>
|
||||
<span style="color:#7f8c8d;font-size:13px;">{{t .Lang "每 5 秒自动刷新"}}</span>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:24px;">
|
||||
<div class="stat-card">
|
||||
<h3>{{.total}}</h3>
|
||||
<p>当前连接总数</p>
|
||||
<p>{{t .Lang "当前连接总数"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{.smtpCount}}</h3>
|
||||
@@ -53,15 +53,15 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>协议</th>
|
||||
<th>来源 IP</th>
|
||||
<th>端口</th>
|
||||
<th>用户名</th>
|
||||
<th>{{t .Lang "协议"}}</th>
|
||||
<th>{{t .Lang "来源 IP"}}</th>
|
||||
<th>{{t .Lang "端口"}}</th>
|
||||
<th>{{t .Lang "用户名"}}</th>
|
||||
<th>TLS</th>
|
||||
<th>连接时间</th>
|
||||
<th>时长</th>
|
||||
<th>最后活跃</th>
|
||||
<th>操作</th>
|
||||
<th>{{t .Lang "连接时间"}}</th>
|
||||
<th>{{t .Lang "时长"}}</th>
|
||||
<th>{{t .Lang "最后活跃"}}</th>
|
||||
<th>{{t .Lang "操作"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -76,19 +76,19 @@
|
||||
<td>{{.IP}}</td>
|
||||
<td>{{.Port}}</td>
|
||||
<td>{{if .User}}{{.User}}{{else}}—{{end}}</td>
|
||||
<td>{{if .TLS}}<span class="badge" style="background:#27ae60;color:#fff;">TLS</span>{{else}}<span class="badge" style="background:#95a5a6;color:#fff;">明文</span>{{end}}</td>
|
||||
<td>{{time12 .Connected}}</td>
|
||||
<td>{{if .TLS}}<span class="badge" style="background:#27ae60;color:#fff;">TLS</span>{{else}}<span class="badge" style="background:#95a5a6;color:#fff;">{{t $.Lang "明文"}}</span>{{end}}</td>
|
||||
<td>{{time12 $.Lang .Connected}}</td>
|
||||
<td>{{durationSeconds ($.now.Sub .Connected)}}s</td>
|
||||
<td>{{time12 .LastActive}}</td>
|
||||
<td>{{time12 $.Lang .LastActive}}</td>
|
||||
<td>
|
||||
<form method="POST" action="/admin/connections/{{.ID}}/disconnect" style="display:inline;"
|
||||
onsubmit="return confirm('确定要断开 IP {{.IP}} 的所有连接并加入黑名单(180 天)吗?');">
|
||||
<button type="submit" class="btn btn-sm btn-danger">断开并封禁</button>
|
||||
onsubmit="return confirm('{{tf $.Lang "确定要断开 IP %s 的所有连接并加入黑名单(180 天)吗?" .IP}}');">
|
||||
<button type="submit" class="btn btn-sm btn-danger">{{t $.Lang "断开并封禁"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="10" style="text-align:center;color:#7f8c8d;">当前没有活动连接</td></tr>
|
||||
<tr><td colspan="10" style="text-align:center;color:#7f8c8d;">{{t $.Lang "当前没有活动连接"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_dashboard"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>管理后台 - MailGo</title>
|
||||
<title>{{t .Lang "管理后台"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,74 +12,74 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 style="margin-bottom:24px;">管理后台</h2>
|
||||
<h2 style="margin-bottom:24px;">{{t .Lang "管理后台"}}</h2>
|
||||
<div style="margin-bottom:24px;">
|
||||
<div class="stat-card">
|
||||
<h3>{{.domainCount}}</h3>
|
||||
<p>域名数</p>
|
||||
<p>{{t .Lang "域名数"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{.userCount}}</h3>
|
||||
<p>用户数</p>
|
||||
<p>{{t .Lang "用户数"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{.totalMails}}</h3>
|
||||
<p>邮件总数</p>
|
||||
<p>{{t .Lang "邮件总数"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{.banCount}}</h3>
|
||||
<p>被封IP</p>
|
||||
<p>{{t .Lang "被封IP"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>邮件分布</h3>
|
||||
<h3>{{t .Lang "邮件分布"}}</h3>
|
||||
<table style="margin-top:12px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文件夹</th>
|
||||
<th>邮件数</th>
|
||||
<th>占用空间</th>
|
||||
<th>{{t .Lang "文件夹"}}</th>
|
||||
<th>{{t .Lang "邮件数"}}</th>
|
||||
<th>{{t .Lang "占用空间"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>收件箱 (INBOX)</td><td>{{.inboxCount}}</td><td>{{formatBytes .inboxSize}}</td></tr>
|
||||
<tr><td>发件箱 (Sent)</td><td>{{.sentCount}}</td><td>{{formatBytes .sentSize}}</td></tr>
|
||||
<tr><td>草稿箱 (Drafts)</td><td>{{.draftsCount}}</td><td>—</td></tr>
|
||||
<tr><td>垃圾箱 (Trash)</td><td>{{.trashCount}}</td><td>—</td></tr>
|
||||
<tr style="font-weight:bold;"><td>合计</td><td>{{.totalMails}}</td><td>{{formatBytes .totalSize}}</td></tr>
|
||||
<tr><td>{{folderLabel .Lang "INBOX"}} (INBOX)</td><td>{{.inboxCount}}</td><td>{{formatBytes .inboxSize}}</td></tr>
|
||||
<tr><td>{{folderLabel .Lang "Sent"}} (Sent)</td><td>{{.sentCount}}</td><td>{{formatBytes .sentSize}}</td></tr>
|
||||
<tr><td>{{folderLabel .Lang "Drafts"}} (Drafts)</td><td>{{.draftsCount}}</td><td>—</td></tr>
|
||||
<tr><td>{{folderLabel .Lang "Trash"}} (Trash)</td><td>{{.trashCount}}</td><td>—</td></tr>
|
||||
<tr style="font-weight:bold;"><td>{{t .Lang "合计"}}</td><td>{{.totalMails}}</td><td>{{formatBytes .totalSize}}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>收发统计</h3>
|
||||
<h3>{{t .Lang "收发统计"}}</h3>
|
||||
<table style="margin-top:12px;">
|
||||
<thead>
|
||||
<tr><th>时间段</th><th>收件</th><th>发件</th></tr>
|
||||
<tr><th>{{t .Lang "时间段"}}</th><th>{{t .Lang "收件"}}</th><th>{{t .Lang "发件"}}</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>今日</td><td>{{.todayReceived}}</td><td>{{.todaySent}}</td></tr>
|
||||
<tr><td>近 7 天</td><td>{{.weekReceived}}</td><td>{{.weekSent}}</td></tr>
|
||||
<tr><td>{{t .Lang "今日"}}</td><td>{{.todayReceived}}</td><td>{{.todaySent}}</td></tr>
|
||||
<tr><td>{{t .Lang "近 7 天"}}</td><td>{{.weekReceived}}</td><td>{{.weekSent}}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>快捷操作</h3>
|
||||
<h3>{{t .Lang "快捷操作"}}</h3>
|
||||
<p style="margin-top:12px;">
|
||||
<a href="/admin/domains/new" class="btn btn-primary">新增域名</a>
|
||||
<a href="/admin/users/new" class="btn btn-primary" style="margin-left:8px;">新增用户</a>
|
||||
<a href="/admin/mails" class="btn btn-primary" style="margin-left:8px;">查看所有邮件</a>
|
||||
<a href="/admin/bans" class="btn btn-primary" style="margin-left:8px;">IP黑名单</a>
|
||||
<a href="/admin/domains/new" class="btn btn-primary">{{t .Lang "新增域名"}}</a>
|
||||
<a href="/admin/users/new" class="btn btn-primary" style="margin-left:8px;">{{t .Lang "新增用户"}}</a>
|
||||
<a href="/admin/mails" class="btn btn-primary" style="margin-left:8px;">{{t .Lang "查看所有邮件"}}</a>
|
||||
<a href="/admin/bans" class="btn btn-primary" style="margin-left:8px;">{{t .Lang "IP黑名单"}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_dns_hint"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>DNS配置 - MailGo</title>
|
||||
<title>{{t .Lang "DNS配置"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,39 +12,39 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<h2 style="margin-bottom:16px;">DNS 配置提示 — {{.domain.Name}}</h2>
|
||||
<p style="margin-bottom:16px;color:#7f8c8d;">请在您的 DNS 服务商处添加以下记录:</p>
|
||||
<h2 style="margin-bottom:16px;">{{tf .Lang "DNS 配置提示 — %s" .domain.Name}}</h2>
|
||||
<p style="margin-bottom:16px;color:#7f8c8d;">{{t .Lang "请在您的 DNS 服务商处添加以下记录:"}}</p>
|
||||
|
||||
<h4 style="margin-bottom:8px;">MX 记录</h4>
|
||||
<h4 style="margin-bottom:8px;">{{t .Lang "MX 记录"}}</h4>
|
||||
<div class="dns-record">@ IN MX 10 mail.{{.domain.Name}}.</div>
|
||||
|
||||
<h4 style="margin-bottom:8px;">SPF 记录 (TXT)</h4>
|
||||
<h4 style="margin-bottom:8px;">{{t .Lang "SPF 记录 (TXT)"}}</h4>
|
||||
<div class="dns-record">@ IN TXT "v=spf1 mx -all"</div>
|
||||
|
||||
<h4 style="margin-bottom:8px;">DKIM 记录 (TXT)</h4>
|
||||
<h4 style="margin-bottom:8px;">{{t .Lang "DKIM 记录 (TXT)"}}</h4>
|
||||
{{if .domain.DkimPublicKey}}
|
||||
<div class="dns-record">default._domainkey.{{.domain.Name}}. IN TXT "{{.dkimRecord}}"</div>
|
||||
{{else}}
|
||||
<div class="dns-record" style="color:#e67e22;">⚠️ DKIM 密钥尚未生成,请编辑域名重新生成。</div>
|
||||
<div class="dns-record" style="color:#e67e22;">{{t .Lang "⚠️ DKIM 密钥尚未生成,请编辑域名重新生成。"}}</div>
|
||||
{{end}}
|
||||
|
||||
<h4 style="margin-bottom:8px;">DMARC 记录</h4>
|
||||
<h4 style="margin-bottom:8px;">{{t .Lang "DMARC 记录"}}</h4>
|
||||
<div class="dns-record">_dmarc.{{.domain.Name}}. IN TXT "v=DMARC1; p=none; rua=mailto:postmaster@{{.domain.Name}}"</div>
|
||||
|
||||
<div style="margin-top:24px;">
|
||||
<a href="/admin/domains" class="btn" style="background:#bdc3c7;color:#fff;">返回域名列表</a>
|
||||
<a href="/admin/domains" class="btn" style="background:#bdc3c7;color:#fff;">{{t .Lang "返回域名列表"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_domain_form"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>{{if .isEdit}}编辑域名{{else}}新增域名{{end}} - MailGo</title>
|
||||
<title>{{if .isEdit}}{{t .Lang "编辑域名"}}{{else}}{{t .Lang "新增域名"}}{{end}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,24 +12,24 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<h2 style="margin-bottom:16px;">{{if .isEdit}}编辑域名{{else}}新增域名{{end}}</h2>
|
||||
<h2 style="margin-bottom:16px;">{{if .isEdit}}{{t .Lang "编辑域名"}}{{else}}{{t .Lang "新增域名"}}{{end}}</h2>
|
||||
{{if .error}}<div class="alert alert-error">{{.error}}</div>{{end}}
|
||||
{{if .caddyMsg}}<div class="alert {{if eq .caddyMsgType "success"}}alert-success{{else}}alert-error{{end}}">{{.caddyMsg}}</div>{{end}}
|
||||
<form method="POST" action="{{if .isEdit}}/admin/domains/{{.domain.ID}}{{else}}/admin/domains{{end}}">
|
||||
<div class="form-group">
|
||||
<label>域名</label>
|
||||
<label>{{t .Lang "域名"}}</label>
|
||||
{{if .isEdit}}
|
||||
<input type="text" name="name" value="{{.domain.Name}}" disabled style="background:#f5f5f5;">
|
||||
{{else}}
|
||||
@@ -37,45 +37,45 @@
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>SMTP 端口</label>
|
||||
<label>{{t .Lang "SMTP 端口"}}</label>
|
||||
<input type="number" name="smtp_port" value="{{.domain.SmtpPort}}" placeholder="25">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>IMAP 端口</label>
|
||||
<label>{{t .Lang "IMAP 端口"}}</label>
|
||||
<input type="number" name="imap_port" value="{{.domain.ImapPort}}" placeholder="143">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>POP3 端口</label>
|
||||
<label>{{t .Lang "POP3 端口"}}</label>
|
||||
<input type="number" name="pop3_port" value="{{.domain.Pop3Port}}" placeholder="110">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="tls_enabled" id="tls_enabled" {{if .domain.TlsEnabled}}checked{{end}} onchange="togglePorts()">
|
||||
启用 TLS
|
||||
{{t .Lang "启用 TLS"}}
|
||||
</label>
|
||||
<p style="color:#7f8c8d;font-size:12px;margin-top:4px;">勾选后端口将自动切换为 SSL/TLS 标准端口</p>
|
||||
<p style="color:#7f8c8d;font-size:12px;margin-top:4px;">{{t .Lang "勾选后端口将自动切换为 SSL/TLS 标准端口"}}</p>
|
||||
</div>
|
||||
{{if .isEdit}}
|
||||
<div id="tls_cert_fields" style="display:none;">
|
||||
<div class="form-group">
|
||||
<label>TLS 私钥 PEM</label>
|
||||
<label>{{t .Lang "TLS 私钥 PEM"}}</label>
|
||||
<textarea name="tls_private_key" rows="8" placeholder="-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----" style="font-family:monospace;"></textarea>
|
||||
<p style="color:#7f8c8d;font-size:12px;margin-top:4px;">私钥不会回显;留空则保留现有私钥。</p>
|
||||
<p style="color:#7f8c8d;font-size:12px;margin-top:4px;">{{t .Lang "私钥不会回显;留空则保留现有私钥。"}}</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>TLS 公钥证书 PEM</label>
|
||||
<label>{{t .Lang "TLS 公钥证书 PEM"}}</label>
|
||||
<textarea name="tls_public_cert" rows="8" placeholder="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----" style="font-family:monospace;">{{.tlsPublicCert}}</textarea>
|
||||
{{if .tlsCertConfigured}}
|
||||
<p style="color:#27ae60;font-size:12px;margin-top:4px;">✅ TLS 证书已配置;上传新证书后自动热加载生效。</p>
|
||||
<p style="color:#27ae60;font-size:12px;margin-top:4px;">{{t .Lang "✅ TLS 证书已配置;上传新证书后自动热加载生效。"}}</p>
|
||||
{{else}}
|
||||
<p style="color:#e67e22;font-size:12px;margin-top:4px;">⚠️ TLS 证书未配置,启用 TLS 时必须同时填写私钥和证书。</p>
|
||||
<p style="color:#e67e22;font-size:12px;margin-top:4px;">{{t .Lang "⚠️ TLS 证书未配置,启用 TLS 时必须同时填写私钥和证书。"}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-top:4px;">
|
||||
<label>从 Caddy 获取证书</label>
|
||||
<p style="color:#7f8c8d;font-size:12px;margin-top:2px;margin-bottom:8px;">若该域名已由本机 Caddy 托管 HTTPS(自动签发证书),可一键导入其证书与私钥,并自动启用 TLS;证书热加载,无需重启服务。</p>
|
||||
<button type="button" class="btn" id="btn_fetch_caddy" onclick="fetchCaddyCert()" style="background:#2e86de;color:#fff;">🔒 从 Caddy 获取证书</button>
|
||||
<label>{{t .Lang "从 Caddy 获取证书"}}</label>
|
||||
<p style="color:#7f8c8d;font-size:12px;margin-top:2px;margin-bottom:8px;">{{t .Lang "若该域名已由本机 Caddy 托管 HTTPS(自动签发证书),可一键导入其证书与私钥,并自动启用 TLS;证书热加载,无需重启服务。"}}</p>
|
||||
<button type="button" class="btn" id="btn_fetch_caddy" onclick="fetchCaddyCert()" style="background:#2e86de;color:#fff;">{{t .Lang "🔒 从 Caddy 获取证书"}}</button>
|
||||
<span id="caddy_fetch_msg" style="margin-left:10px;font-size:12px;color:#7f8c8d;"></span>
|
||||
<script>
|
||||
async function fetchCaddyCert() {
|
||||
@@ -83,7 +83,7 @@
|
||||
var msg = document.getElementById('caddy_fetch_msg');
|
||||
btn.disabled = true;
|
||||
var oldText = btn.textContent;
|
||||
btn.textContent = '获取中…';
|
||||
btn.textContent = '{{t .Lang "获取中…"}}';
|
||||
msg.textContent = '';
|
||||
try {
|
||||
var resp = await fetch('/admin/domains/{{.domain.ID}}/fetch-caddy-cert', { method: 'POST' });
|
||||
@@ -91,9 +91,9 @@
|
||||
window.location.href = resp.url;
|
||||
return;
|
||||
}
|
||||
msg.textContent = '获取失败: ' + await resp.text();
|
||||
msg.textContent = '{{t .Lang "获取失败"}}: ' + await resp.text();
|
||||
} catch (e) {
|
||||
msg.textContent = '请求失败: ' + e;
|
||||
msg.textContent = '{{t .Lang "请求失败"}}: ' + e;
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.textContent = oldText;
|
||||
@@ -118,17 +118,17 @@
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="regenerate_dkim">
|
||||
重新生成 DKIM 密钥
|
||||
{{t .Lang "重新生成 DKIM 密钥"}}
|
||||
</label>
|
||||
{{if .domain.DkimPublicKey}}
|
||||
<p style="color:#27ae60;font-size:12px;margin-top:4px;">✅ DKIM 密钥已配置</p>
|
||||
<p style="color:#27ae60;font-size:12px;margin-top:4px;">{{t .Lang "✅ DKIM 密钥已配置"}}</p>
|
||||
{{else}}
|
||||
<p style="color:#e67e22;font-size:12px;margin-top:4px;">⚠️ DKIM 密钥未配置</p>
|
||||
<p style="color:#e67e22;font-size:12px;margin-top:4px;">{{t .Lang "⚠️ DKIM 密钥未配置"}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<button type="submit" class="btn btn-primary">{{if .isEdit}}保存更改{{else}}创建域名{{end}}</button>
|
||||
<a href="/admin/domains" class="btn" style="margin-left:8px;background:#bdc3c7;color:#fff;">取消</a>
|
||||
<button type="submit" class="btn btn-primary">{{if .isEdit}}{{t .Lang "保存更改"}}{{else}}{{t .Lang "创建域名"}}{{end}}</button>
|
||||
<a href="/admin/domains" class="btn" style="margin-left:8px;background:#bdc3c7;color:#fff;">{{t .Lang "取消"}}</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_domains"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>域名管理 - MailGo</title>
|
||||
<title>{{t .Lang "域名管理"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,35 +12,35 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
|
||||
<h2>域名列表</h2>
|
||||
<a href="/admin/domains/new" class="btn btn-primary">新增域名</a>
|
||||
<h2>{{t .Lang "域名列表"}}</h2>
|
||||
<a href="/admin/domains/new" class="btn btn-primary">{{t .Lang "新增域名"}}</a>
|
||||
</div>
|
||||
{{if not .domains}}
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">暂无域名</p>
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">{{t $.Lang "暂无域名"}}</p>
|
||||
{{else}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>域名</th>
|
||||
<th>{{t $.Lang "域名"}}</th>
|
||||
<th>SMTP</th>
|
||||
<th>IMAP</th>
|
||||
<th>POP3</th>
|
||||
<th>TLS</th>
|
||||
<th>操作</th>
|
||||
<th>{{t $.Lang "操作"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -53,11 +53,11 @@
|
||||
<td>{{.Pop3Port}}</td>
|
||||
<td>{{if .TlsEnabled}}✅{{else}}❌{{end}}</td>
|
||||
<td>
|
||||
<a href="/admin/domains/{{.ID}}/edit" class="btn btn-primary btn-sm">编辑</a>
|
||||
<a href="/admin/domains/{{.ID}}/edit" class="btn btn-primary btn-sm">{{t $.Lang "编辑"}}</a>
|
||||
<a href="/admin/domains/{{.ID}}/dns" class="btn btn-primary btn-sm">DNS</a>
|
||||
<form method="POST" action="/admin/domains/{{.ID}}/delete" style="display:inline;"
|
||||
onsubmit="return confirm('确定要删除域名 {{.Name}} 吗?删除域名将导致关联用户无法收发邮件!');">
|
||||
<button type="submit" class="btn btn-danger btn-sm">删除</button>
|
||||
onsubmit="return confirm('{{tf $.Lang "确定要删除域名 %s 吗?删除域名将导致关联用户无法收发邮件!" .Name}}');">
|
||||
<button type="submit" class="btn btn-danger btn-sm">{{t $.Lang "删除"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -69,11 +69,11 @@
|
||||
{{if .totalPages}}
|
||||
<div class="pagination">
|
||||
{{if gt .page 1}}
|
||||
<a href="/admin/domains?page={{sub .page 1}}">上一页</a>
|
||||
<a href="/admin/domains?page={{sub .page 1}}">{{t $.Lang "上一页"}}</a>
|
||||
{{end}}
|
||||
<span>第 {{.page}} / {{.totalPages}} 页</span>
|
||||
<span>{{tf $.Lang "第 %d / %d 页" .page .totalPages}}</span>
|
||||
{{if lt .page .totalPages}}
|
||||
<a href="/admin/domains?page={{add .page 1}}">下一页</a>
|
||||
<a href="/admin/domains?page={{add .page 1}}">{{t $.Lang "下一页"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_mail_view"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>查看邮件 - MailGo 管理后台</title>
|
||||
<title>{{t .Lang "查看邮件"}} - MailGo {{t .Lang "管理后台"}}</title>
|
||||
{{template "styles" .}}
|
||||
<style>
|
||||
.mail-body-iframe {
|
||||
@@ -21,29 +21,29 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" class="active">所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" class="active">{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<div style="margin-bottom:16px;">
|
||||
<a href="/admin/mails" class="btn" style="background:#bdc3c7;color:#fff;">返回邮件列表</a>
|
||||
<a href="/admin/mails" class="btn" style="background:#bdc3c7;color:#fff;">{{t .Lang "返回邮件列表"}}</a>
|
||||
</div>
|
||||
<h2>{{if .message.Subject}}{{.message.Subject}}{{else}}(无主题){{end}}</h2>
|
||||
<h2>{{if .message.Subject}}{{.message.Subject}}{{else}}{{t .Lang "(无主题)"}}{{end}}</h2>
|
||||
<div class="mail-meta" style="margin-top:12px;">
|
||||
<p><strong>发件人:</strong> {{decodeHeader .message.FromAddr}}</p>
|
||||
<p><strong>收件人:</strong> {{.message.ToAddr}}</p>
|
||||
{{if .message.CcAddr}}<p><strong>抄送:</strong> {{.message.CcAddr}}</p>{{end}}
|
||||
<p><strong>所属用户:</strong> {{if .message.User.ID}}{{.message.User.Username}}{{else}}—{{end}}</p>
|
||||
<p><strong>文件夹:</strong> {{.message.Folder}}</p>
|
||||
<p><strong>时间:</strong> {{time12 .message.Date}}</p>
|
||||
<p><strong>{{t .Lang "发件人:"}}</strong> {{decodeHeader .message.FromAddr}}</p>
|
||||
<p><strong>{{t .Lang "收件人:"}}</strong> {{.message.ToAddr}}</p>
|
||||
{{if .message.CcAddr}}<p><strong>{{t .Lang "抄送:"}}</strong> {{.message.CcAddr}}</p>{{end}}
|
||||
<p><strong>{{t .Lang "所属用户:"}}</strong> {{if .message.User.ID}}{{.message.User.Username}}{{else}}—{{end}}</p>
|
||||
<p><strong>{{t .Lang "文件夹:"}}</strong> {{.message.Folder}}</p>
|
||||
<p><strong>{{t .Lang "时间:"}}</strong> {{time12 .Lang .message.Date}}</p>
|
||||
</div>
|
||||
<div class="mail-body">
|
||||
{{if .message.HtmlBody}}
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
{{if .attachments}}
|
||||
<div class="attachment-list">
|
||||
<h4 style="margin-bottom:8px;">附件</h4>
|
||||
<h4 style="margin-bottom:8px;">{{t .Lang "附件列表"}}</h4>
|
||||
{{range .attachments}}
|
||||
<div class="attachment-item">
|
||||
📎 <a href="/admin/attachment/{{.ID}}">{{.FileName}}</a>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_mails"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>所有邮件 - MailGo</title>
|
||||
<title>{{t .Lang "所有邮件"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,38 +12,38 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<h2 style="margin-bottom:16px;">所有邮件</h2>
|
||||
<h2 style="margin-bottom:16px;">{{t .Lang "所有邮件"}}</h2>
|
||||
<div style="margin-bottom:16px;">
|
||||
<a href="/admin/mails" {{if eq .folder ""}}class="btn btn-primary"{{else}}class="btn" style="background:#ecf0f1;color:#333;"{{end}}>全部</a>
|
||||
<a href="/admin/mails" {{if eq .folder ""}}class="btn btn-primary"{{else}}class="btn" style="background:#ecf0f1;color:#333;"{{end}}>{{t .Lang "全部"}}</a>
|
||||
<a href="/admin/mails?folder=INBOX" {{if eq .folder "INBOX"}}class="btn btn-primary"{{else}}class="btn" style="background:#ecf0f1;color:#333;"{{end}}>INBOX</a>
|
||||
<a href="/admin/mails?folder=Sent" {{if eq .folder "Sent"}}class="btn btn-primary"{{else}}class="btn" style="background:#ecf0f1;color:#333;"{{end}}>Sent</a>
|
||||
<a href="/admin/mails?folder=Drafts" {{if eq .folder "Drafts"}}class="btn btn-primary"{{else}}class="btn" style="background:#ecf0f1;color:#333;"{{end}}>Drafts</a>
|
||||
<a href="/admin/mails?folder=Trash" {{if eq .folder "Trash"}}class="btn btn-primary"{{else}}class="btn" style="background:#ecf0f1;color:#333;"{{end}}>Trash</a>
|
||||
</div>
|
||||
{{if not .messages}}
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">暂无邮件</p>
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">{{t $.Lang "暂无邮件"}}</p>
|
||||
{{else}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>发件人</th>
|
||||
<th>收件人</th>
|
||||
<th>主题</th>
|
||||
<th>所属用户</th>
|
||||
<th>文件夹</th>
|
||||
<th>日期</th>
|
||||
<th>{{t $.Lang "发件人"}}</th>
|
||||
<th>{{t $.Lang "收件人"}}</th>
|
||||
<th>{{t $.Lang "主题"}}</th>
|
||||
<th>{{t $.Lang "所属用户"}}</th>
|
||||
<th>{{t $.Lang "文件夹"}}</th>
|
||||
<th>{{t $.Lang "日期"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -54,7 +54,7 @@
|
||||
<td>{{.Subject}}</td>
|
||||
<td>{{if .User.ID}}{{.User.Username}}{{else}}—{{end}}</td>
|
||||
<td>{{.Folder}}</td>
|
||||
<td>{{time12m .Date}}</td>
|
||||
<td>{{time12m $.Lang .Date}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
@@ -64,11 +64,11 @@
|
||||
{{if .totalPages}}
|
||||
<div class="pagination">
|
||||
{{if gt .page 1}}
|
||||
<a href="/admin/mails?page={{sub .page 1}}{{if .folder}}&folder={{.folder}}{{end}}">上一页</a>
|
||||
<a href="/admin/mails?page={{sub .page 1}}{{if .folder}}&folder={{.folder}}{{end}}">{{t $.Lang "上一页"}}</a>
|
||||
{{end}}
|
||||
<span>第 {{.page}} / {{.totalPages}} 页</span>
|
||||
<span>{{tf $.Lang "第 %d / %d 页" .page .totalPages}}</span>
|
||||
{{if lt .page .totalPages}}
|
||||
<a href="/admin/mails?page={{add .page 1}}{{if .folder}}&folder={{.folder}}{{end}}">下一页</a>
|
||||
<a href="/admin/mails?page={{add .page 1}}{{if .folder}}&folder={{.folder}}{{end}}">{{t $.Lang "下一页"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_outbound"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>外发队列 - MailGo</title>
|
||||
<title>{{t .Lang "外发队列"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,60 +12,60 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 style="margin-bottom:24px;">外发队列</h2>
|
||||
<h2 style="margin-bottom:24px;">{{t .Lang "外发队列"}}</h2>
|
||||
|
||||
<div style="margin-bottom:24px;">
|
||||
<div class="stat-card">
|
||||
<h3>{{index .statCounts "pending"}}</h3>
|
||||
<p>待发送</p>
|
||||
<p>{{t .Lang "待发送"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{index .statCounts "deferred"}}</h3>
|
||||
<p>等待重试</p>
|
||||
<p>{{t .Lang "等待重试"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{index .statCounts "sent"}}</h3>
|
||||
<p>已送达</p>
|
||||
<p>{{t .Lang "已送达"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{index .statCounts "failed"}}</h3>
|
||||
<p>失败</p>
|
||||
<p>{{t .Lang "失败"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div style="margin-bottom:12px;">
|
||||
<a href="/admin/outbound" class="btn btn-sm {{if eq .status ""}}btn-primary{{end}}" style="background:{{if eq .status ""}}#3498db{{else}}#ecf0f1{{end}};color:{{if eq .status ""}}#fff{{else}}#333{{end}};">全部</a>
|
||||
<a href="/admin/outbound?status=pending" class="btn btn-sm">待发送</a>
|
||||
<a href="/admin/outbound?status=deferred" class="btn btn-sm">等待重试</a>
|
||||
<a href="/admin/outbound?status=sent" class="btn btn-sm">已送达</a>
|
||||
<a href="/admin/outbound?status=failed" class="btn btn-sm">失败</a>
|
||||
<a href="/admin/outbound?status=canceled" class="btn btn-sm">已取消</a>
|
||||
<a href="/admin/outbound" class="btn btn-sm {{if eq .status ""}}btn-primary{{end}}" style="background:{{if eq .status ""}}#3498db{{else}}#ecf0f1{{end}};color:{{if eq .status ""}}#fff{{else}}#333{{end}};">{{t .Lang "全部"}}</a>
|
||||
<a href="/admin/outbound?status=pending" class="btn btn-sm">{{t .Lang "待发送"}}</a>
|
||||
<a href="/admin/outbound?status=deferred" class="btn btn-sm">{{t .Lang "等待重试"}}</a>
|
||||
<a href="/admin/outbound?status=sent" class="btn btn-sm">{{t .Lang "已送达"}}</a>
|
||||
<a href="/admin/outbound?status=failed" class="btn btn-sm">{{t .Lang "失败"}}</a>
|
||||
<a href="/admin/outbound?status=canceled" class="btn btn-sm">{{t .Lang "已取消"}}</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>发件人</th>
|
||||
<th>收件人</th>
|
||||
<th>状态</th>
|
||||
<th>尝试次数</th>
|
||||
<th>下次重试</th>
|
||||
<th>最后响应 / 错误</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
<th>{{t .Lang "发件人"}}</th>
|
||||
<th>{{t .Lang "收件人"}}</th>
|
||||
<th>{{t .Lang "状态"}}</th>
|
||||
<th>{{t .Lang "尝试次数"}}</th>
|
||||
<th>{{t .Lang "下次重试"}}</th>
|
||||
<th>{{t .Lang "最后响应 / 错误"}}</th>
|
||||
<th>{{t .Lang "创建时间"}}</th>
|
||||
<th>{{t .Lang "操作"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -75,40 +75,40 @@
|
||||
<td>{{.FromAddr}}</td>
|
||||
<td>{{.ToAddr}}</td>
|
||||
<td>
|
||||
{{if eq .Status "pending"}}<span class="badge" style="background:#f39c12;color:#fff;">{{call $.statusText .Status}}</span>
|
||||
{{else if eq .Status "deferred"}}<span class="badge" style="background:#e67e22;color:#fff;">{{call $.statusText .Status}}</span>
|
||||
{{else if eq .Status "sent"}}<span class="badge" style="background:#27ae60;color:#fff;">{{call $.statusText .Status}}</span>
|
||||
{{else if eq .Status "failed"}}<span class="badge badge-unread">{{call $.statusText .Status}}</span>
|
||||
{{else}}<span class="badge" style="background:#95a5a6;color:#fff;">{{call $.statusText .Status}}</span>{{end}}
|
||||
{{if eq .Status "pending"}}<span class="badge" style="background:#f39c12;color:#fff;">{{t $.Lang (call $.statusText .Status)}}</span>
|
||||
{{else if eq .Status "deferred"}}<span class="badge" style="background:#e67e22;color:#fff;">{{t $.Lang (call $.statusText .Status)}}</span>
|
||||
{{else if eq .Status "sent"}}<span class="badge" style="background:#27ae60;color:#fff;">{{t $.Lang (call $.statusText .Status)}}</span>
|
||||
{{else if eq .Status "failed"}}<span class="badge badge-unread">{{t $.Lang (call $.statusText .Status)}}</span>
|
||||
{{else}}<span class="badge" style="background:#95a5a6;color:#fff;">{{t $.Lang (call $.statusText .Status)}}</span>{{end}}
|
||||
</td>
|
||||
<td>{{.Attempts}}</td>
|
||||
<td>{{if or (eq .Status "pending") (eq .Status "deferred")}}{{time12m .NextAttemptAt}}{{else}}—{{end}}</td>
|
||||
<td>{{if or (eq .Status "pending") (eq .Status "deferred")}}{{time12m $.Lang .NextAttemptAt}}{{else}}—{{end}}</td>
|
||||
<td style="max-width:280px;word-break:break-all;">{{if .LastResponse}}{{.LastResponse}}{{else}}{{.LastError}}{{end}}</td>
|
||||
<td>{{time12m .CreatedAt}}</td>
|
||||
<td>{{time12m $.Lang .CreatedAt}}</td>
|
||||
<td>
|
||||
{{if or (eq .Status "failed") (eq .Status "deferred") (eq .Status "canceled") (eq .Status "pending")}}
|
||||
<form method="POST" action="/admin/outbound/{{.ID}}/retry" style="display:inline;">
|
||||
<button type="submit" class="btn btn-sm btn-primary">重试</button>
|
||||
<button type="submit" class="btn btn-sm btn-primary">{{t $.Lang "重试"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if or (eq .Status "pending") (eq .Status "deferred")}}
|
||||
<form method="POST" action="/admin/outbound/{{.ID}}/cancel" style="display:inline;" onsubmit="return confirm('确认取消该投递任务?');">
|
||||
<button type="submit" class="btn btn-sm btn-danger">取消</button>
|
||||
<form method="POST" action="/admin/outbound/{{.ID}}/cancel" style="display:inline;" onsubmit="return confirm('{{t $.Lang "确认取消该投递任务?"}}');">
|
||||
<button type="submit" class="btn btn-sm btn-danger">{{t $.Lang "取消"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="9" style="text-align:center;color:#7f8c8d;">队列为空</td></tr>
|
||||
<tr><td colspan="9" style="text-align:center;color:#7f8c8d;">{{t $.Lang "队列为空"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{if gt .totalPages 1}}
|
||||
<div class="pagination">
|
||||
{{if gt .page 1}}<a href="/admin/outbound?page={{sub .page 1}}&status={{.status}}">上一页</a>{{end}}
|
||||
<span class="current">第 {{.page}} / {{.totalPages}} 页</span>
|
||||
{{if lt .page .totalPages}}<a href="/admin/outbound?page={{add .page 1}}&status={{.status}}">下一页</a>{{end}}
|
||||
{{if gt .page 1}}<a href="/admin/outbound?page={{sub .page 1}}&status={{.status}}">{{t $.Lang "上一页"}}</a>{{end}}
|
||||
<span class="current">{{tf $.Lang "第 %d / %d 页" .page .totalPages}}</span>
|
||||
{{if lt .page .totalPages}}<a href="/admin/outbound?page={{add .page 1}}&status={{.status}}">{{t $.Lang "下一页"}}</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_protocol_logs"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>协议日志 - MailGo</title>
|
||||
<title>{{t .Lang "协议日志"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,41 +12,41 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
|
||||
<h2>协议日志(SMTP / IMAP / POP3)</h2>
|
||||
<h2>{{t .Lang "协议日志(SMTP / IMAP / POP3)"}}</h2>
|
||||
<form method="POST" action="/admin/protocol-logs/cleanup" style="display:inline;"
|
||||
onsubmit="return confirm('确认清理 {{.keepDays}} 天前的协议日志?');">
|
||||
<button type="submit" class="btn btn-primary">清理旧日志</button>
|
||||
onsubmit="return confirm('{{tf .Lang "确认清理 %d 天前的协议日志?" .keepDays}}');">
|
||||
<button type="submit" class="btn btn-primary">{{t .Lang "清理旧日志"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:24px;">
|
||||
<div class="stat-card">
|
||||
<h3>{{index .todayStats "smtp" "fail"}}</h3>
|
||||
<p>今日 SMTP 失败</p>
|
||||
<p>{{t .Lang "今日 SMTP 失败"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{index .todayStats "imap" "fail"}}</h3>
|
||||
<p>今日 IMAP 失败</p>
|
||||
<p>{{t .Lang "今日 IMAP 失败"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{index .todayStats "pop3" "fail"}}</h3>
|
||||
<p>今日 POP3 失败</p>
|
||||
<p>{{t .Lang "今日 POP3 失败"}}</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>{{add (add (index .allStats "smtp" "fail") (index .allStats "imap" "fail")) (index .allStats "pop3" "fail")}}</h3>
|
||||
<p>历史失败(全部)</p>
|
||||
<p>{{t .Lang "历史失败(全部)"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,45 +54,45 @@
|
||||
<form method="GET" action="/admin/protocol-logs" style="margin-bottom:16px;">
|
||||
<div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center;">
|
||||
<select name="protocol" style="padding:6px 10px;">
|
||||
<option value="">全部协议</option>
|
||||
<option value="">{{t .Lang "全部协议"}}</option>
|
||||
<option value="smtp" {{if eq .filter.protocol "smtp"}}selected{{end}}>SMTP</option>
|
||||
<option value="imap" {{if eq .filter.protocol "imap"}}selected{{end}}>IMAP</option>
|
||||
<option value="pop3" {{if eq .filter.protocol "pop3"}}selected{{end}}>POP3</option>
|
||||
</select>
|
||||
<select name="success" style="padding:6px 10px;">
|
||||
<option value="">全部状态</option>
|
||||
<option value="success" {{if eq .filter.success "success"}}selected{{end}}>成功</option>
|
||||
<option value="fail" {{if eq .filter.success "fail"}}selected{{end}}>失败</option>
|
||||
<option value="">{{t .Lang "全部状态"}}</option>
|
||||
<option value="success" {{if eq .filter.success "success"}}selected{{end}}>{{t .Lang "成功"}}</option>
|
||||
<option value="fail" {{if eq .filter.success "fail"}}selected{{end}}>{{t .Lang "失败"}}</option>
|
||||
</select>
|
||||
<input type="text" name="ip" placeholder="来源 IP(模糊)" value="{{.filter.ip}}" style="padding:6px 10px;width:160px;">
|
||||
<input type="text" name="username" placeholder="用户名(模糊)" value="{{.filter.username}}" style="padding:6px 10px;width:160px;">
|
||||
<input type="text" name="ip" placeholder="{{t .Lang "来源 IP(模糊)"}}" value="{{.filter.ip}}" style="padding:6px 10px;width:160px;">
|
||||
<input type="text" name="username" placeholder="{{t .Lang "用户名(模糊)"}}" value="{{.filter.username}}" style="padding:6px 10px;width:160px;">
|
||||
<input type="date" name="from" value="{{.filter.from}}" style="padding:6px 10px;">
|
||||
<span>至</span>
|
||||
<span>{{t .Lang "至"}}</span>
|
||||
<input type="date" name="to" value="{{.filter.to}}" style="padding:6px 10px;">
|
||||
<button type="submit" class="btn btn-sm btn-primary">筛选</button>
|
||||
<a href="/admin/protocol-logs" class="btn btn-sm">重置</a>
|
||||
<button type="submit" class="btn btn-sm btn-primary">{{t .Lang "筛选"}}</button>
|
||||
<a href="/admin/protocol-logs" class="btn btn-sm">{{t .Lang "重置"}}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>协议</th>
|
||||
<th>端口</th>
|
||||
<th>来源 IP</th>
|
||||
<th>用户名</th>
|
||||
<th>状态</th>
|
||||
<th>失败原因</th>
|
||||
<th>操作摘要</th>
|
||||
<th>消息数</th>
|
||||
<th>时长</th>
|
||||
<th>{{t .Lang "时间"}}</th>
|
||||
<th>{{t .Lang "协议"}}</th>
|
||||
<th>{{t .Lang "端口"}}</th>
|
||||
<th>{{t .Lang "来源 IP"}}</th>
|
||||
<th>{{t .Lang "用户名"}}</th>
|
||||
<th>{{t .Lang "状态"}}</th>
|
||||
<th>{{t .Lang "失败原因"}}</th>
|
||||
<th>{{t .Lang "操作摘要"}}</th>
|
||||
<th>{{t .Lang "消息数"}}</th>
|
||||
<th>{{t .Lang "时长"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .logs}}
|
||||
<tr>
|
||||
<td style="white-space:nowrap;">{{time12 .CreatedAt}}</td>
|
||||
<td style="white-space:nowrap;">{{time12 $.Lang .CreatedAt}}</td>
|
||||
<td>
|
||||
{{if eq .Protocol "smtp"}}<span class="badge" style="background:#3498db;color:#fff;">SMTP</span>
|
||||
{{else if eq .Protocol "imap"}}<span class="badge" style="background:#9b59b6;color:#fff;">IMAP</span>
|
||||
@@ -102,8 +102,8 @@
|
||||
<td>{{.ClientIP}}</td>
|
||||
<td>{{if .Username}}{{.Username}}{{else}}—{{end}}</td>
|
||||
<td>
|
||||
{{if .Success}}<span class="badge" style="background:#27ae60;color:#fff;">成功</span>
|
||||
{{else}}<span class="badge badge-unread">失败</span>{{end}}
|
||||
{{if .Success}}<span class="badge" style="background:#27ae60;color:#fff;">{{t $.Lang "成功"}}</span>
|
||||
{{else}}<span class="badge badge-unread">{{t $.Lang "失败"}}</span>{{end}}
|
||||
</td>
|
||||
<td style="max-width:200px;">{{if .FailReason}}{{.FailReason}}{{else}}—{{end}}</td>
|
||||
<td style="max-width:320px;word-break:break-all;font-size:13px;color:#555;">{{.Detail}}</td>
|
||||
@@ -111,16 +111,16 @@
|
||||
<td>{{if .DurationMs}}{{div .DurationMs 1000}}s{{else}}—{{end}}</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="10" style="text-align:center;color:#7f8c8d;">暂无记录</td></tr>
|
||||
<tr><td colspan="10" style="text-align:center;color:#7f8c8d;">{{t $.Lang "暂无记录"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{if gt .totalPages 1}}
|
||||
<div class="pagination">
|
||||
{{if gt .page 1}}<a href="/admin/protocol-logs?page={{sub .page 1}}&protocol={{.filter.protocol}}&success={{.filter.success}}&ip={{.filter.ip}}&username={{.filter.username}}&from={{.filter.from}}&to={{.filter.to}}">上一页</a>{{end}}
|
||||
<span class="current">第 {{.page}} / {{.totalPages}} 页(共 {{.total}} 条)</span>
|
||||
{{if lt .page .totalPages}}<a href="/admin/protocol-logs?page={{add .page 1}}&protocol={{.filter.protocol}}&success={{.filter.success}}&ip={{.filter.ip}}&username={{.filter.username}}&from={{.filter.from}}&to={{.filter.to}}">下一页</a>{{end}}
|
||||
{{if gt .page 1}}<a href="/admin/protocol-logs?page={{sub .page 1}}&protocol={{.filter.protocol}}&success={{.filter.success}}&ip={{.filter.ip}}&username={{.filter.username}}&from={{.filter.from}}&to={{.filter.to}}">{{t $.Lang "上一页"}}</a>{{end}}
|
||||
<span class="current">{{tf $.Lang "第 %d / %d 页(共 %d 条)" .page .totalPages .total}}</span>
|
||||
{{if lt .page .totalPages}}<a href="/admin/protocol-logs?page={{add .page 1}}&protocol={{.filter.protocol}}&success={{.filter.success}}&ip={{.filter.ip}}&username={{.filter.username}}&from={{.filter.from}}&to={{.filter.to}}">{{t $.Lang "下一页"}}</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_user_form"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>{{if .isEdit}}编辑用户{{else}}新增用户{{end}} - MailGo</title>
|
||||
<title>{{if .isEdit}}{{t .Lang "编辑用户"}}{{else}}{{t .Lang "新增用户"}}{{end}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,58 +12,67 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<h2 style="margin-bottom:16px;">{{if .isEdit}}编辑用户{{else}}新增用户{{end}}</h2>
|
||||
<h2 style="margin-bottom:16px;">{{if .isEdit}}{{t .Lang "编辑用户"}}{{else}}{{t .Lang "新增用户"}}{{end}}</h2>
|
||||
{{if .error}}<div class="alert alert-error">{{.error}}</div>{{end}}
|
||||
<form method="POST" action="{{if .isEdit}}/admin/users/{{.user.ID}}{{else}}/admin/users{{end}}">
|
||||
<div class="form-group">
|
||||
<label>用户名</label>
|
||||
<label>{{t .Lang "用户名"}}</label>
|
||||
<input type="text" name="username" required value="{{.user.Username}}" placeholder="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{if .isEdit}}新密码(留空则不修改){{else}}密码{{end}}</label>
|
||||
<input type="password" name="password" {{if not .isEdit}}required{{end}} placeholder="请输入密码">
|
||||
<label>{{if .isEdit}}{{t .Lang "新密码(留空则不修改)"}}{{else}}{{t .Lang "密码"}}{{end}}</label>
|
||||
<input type="password" name="password" {{if not .isEdit}}required{{end}} placeholder="{{t .Lang "请输入密码"}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>域名</label>
|
||||
<label>{{t .Lang "界面语言"}}</label>
|
||||
<select name="language">
|
||||
<option value="auto" {{if eq .user.Language "auto"}}selected{{end}}>{{t .Lang "自动(跟随浏览器)"}}</option>
|
||||
<option value="en" {{if eq .user.Language "en"}}selected{{end}}>English</option>
|
||||
<option value="zh" {{if eq .user.Language "zh"}}selected{{end}}>中文</option>
|
||||
<option value="ja" {{if eq .user.Language "ja"}}selected{{end}}>日本語</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{t .Lang "域名"}}</label>
|
||||
<select name="domain_id" required>
|
||||
<option value="">请选择域名</option>
|
||||
<option value="">{{t .Lang "请选择域名"}}</option>
|
||||
{{range .domains}}
|
||||
<option value="{{.ID}}" {{if eq .ID $.user.DomainID}}selected{{end}}>{{.Name}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>配额 (GB)</label>
|
||||
<label>{{t .Lang "配额 (GB)"}}</label>
|
||||
<input type="number" name="quota_gb" min="1" value="5" placeholder="5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="is_admin" {{if .user.IsAdmin}}checked{{end}}>
|
||||
管理员
|
||||
{{t .Lang "管理员"}}
|
||||
</label>
|
||||
</div>
|
||||
{{if .isEdit}}
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="is_active" {{if .user.IsActive}}checked{{end}}>
|
||||
启用账户
|
||||
{{t .Lang "启用账户"}}
|
||||
</label>
|
||||
</div>
|
||||
{{end}}
|
||||
<button type="submit" class="btn btn-primary">{{if .isEdit}}保存更改{{else}}创建用户{{end}}</button>
|
||||
<a href="/admin/users" class="btn" style="margin-left:8px;background:#bdc3c7;color:#fff;">取消</a>
|
||||
<button type="submit" class="btn btn-primary">{{if .isEdit}}{{t .Lang "保存更改"}}{{else}}{{t .Lang "创建用户"}}{{end}}</button>
|
||||
<a href="/admin/users" class="btn" style="margin-left:8px;background:#bdc3c7;color:#fff;">{{t .Lang "取消"}}</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "admin_users"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>用户管理 - MailGo</title>
|
||||
<title>{{t .Lang "用户管理"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,36 +12,36 @@
|
||||
<div class="container">
|
||||
<div class="clearfix">
|
||||
<div class="sidebar">
|
||||
<a href="/inbox">返回邮箱</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>控制面板</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>域名管理</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>用户管理</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>所有邮件</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>外发队列</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>协议日志</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>当前连接</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>IP黑名单</a>
|
||||
<a href="/inbox">{{t .Lang "返回邮箱"}}</a>
|
||||
<a href="/admin" {{if eq .activeFolder "admin"}}class="active"{{end}}>{{t .Lang "控制面板"}}</a>
|
||||
<a href="/admin/domains" {{if eq .activeFolder "domains"}}class="active"{{end}}>{{t .Lang "域名管理"}}</a>
|
||||
<a href="/admin/users" {{if eq .activeFolder "users"}}class="active"{{end}}>{{t .Lang "用户管理"}}</a>
|
||||
<a href="/admin/mails" {{if eq .activeFolder "mails"}}class="active"{{end}}>{{t .Lang "所有邮件"}}</a>
|
||||
<a href="/admin/outbound" {{if eq .activeFolder "outbound"}}class="active"{{end}}>{{t .Lang "外发队列"}}</a>
|
||||
<a href="/admin/protocol-logs" {{if eq .activeFolder "protocol-logs"}}class="active"{{end}}>{{t .Lang "协议日志"}}</a>
|
||||
<a href="/admin/connections" {{if eq .activeFolder "connections"}}class="active"{{end}}>{{t .Lang "当前连接"}}</a>
|
||||
<a href="/admin/bans" {{if eq .activeFolder "bans"}}class="active"{{end}}>{{t .Lang "IP黑名单"}}</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
|
||||
<h2>用户列表</h2>
|
||||
<a href="/admin/users/new" class="btn btn-primary">新增用户</a>
|
||||
<h2>{{t .Lang "用户列表"}}</h2>
|
||||
<a href="/admin/users/new" class="btn btn-primary">{{t .Lang "新增用户"}}</a>
|
||||
</div>
|
||||
{{if not .users}}
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">暂无用户</p>
|
||||
<p style="color:#7f8c8d;text-align:center;padding:40px 0;">{{t $.Lang "暂无用户"}}</p>
|
||||
{{else}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>用户名</th>
|
||||
<th>域名ID</th>
|
||||
<th>配额</th>
|
||||
<th>已用</th>
|
||||
<th>状态</th>
|
||||
<th>管理员</th>
|
||||
<th>操作</th>
|
||||
<th>{{t $.Lang "用户名"}}</th>
|
||||
<th>{{t $.Lang "域名ID"}}</th>
|
||||
<th>{{t $.Lang "配额"}}</th>
|
||||
<th>{{t $.Lang "已用"}}</th>
|
||||
<th>{{t $.Lang "状态"}}</th>
|
||||
<th>{{t $.Lang "管理员"}}</th>
|
||||
<th>{{t $.Lang "操作"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -55,10 +55,10 @@
|
||||
<td>{{if .IsActive}}✅{{else}}❌{{end}}</td>
|
||||
<td>{{if .IsAdmin}}✅{{else}}—{{end}}</td>
|
||||
<td>
|
||||
<a href="/admin/users/{{.ID}}/edit" class="btn btn-primary btn-sm">编辑</a>
|
||||
<a href="/admin/users/{{.ID}}/edit" class="btn btn-primary btn-sm">{{t $.Lang "编辑"}}</a>
|
||||
<form method="POST" action="/admin/users/{{.ID}}/delete" style="display:inline;"
|
||||
onsubmit="return confirm('确定要删除用户 {{.Username}} 吗?');">
|
||||
<button type="submit" class="btn btn-danger btn-sm">删除</button>
|
||||
onsubmit="return confirm('{{tf $.Lang "确定要删除用户 %s 吗?" .Username}}');">
|
||||
<button type="submit" class="btn btn-danger btn-sm">{{t $.Lang "删除"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -70,11 +70,11 @@
|
||||
{{if .totalPages}}
|
||||
<div class="pagination">
|
||||
{{if gt .page 1}}
|
||||
<a href="/admin/users?page={{sub .page 1}}">上一页</a>
|
||||
<a href="/admin/users?page={{sub .page 1}}">{{t $.Lang "上一页"}}</a>
|
||||
{{end}}
|
||||
<span>第 {{.page}} / {{.totalPages}} 页</span>
|
||||
<span>{{tf $.Lang "第 %d / %d 页" .page .totalPages}}</span>
|
||||
{{if lt .page .totalPages}}
|
||||
<a href="/admin/users?page={{add .page 1}}">下一页</a>
|
||||
<a href="/admin/users?page={{add .page 1}}">{{t $.Lang "下一页"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "banned"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>访问被禁止 - MailGo</title>
|
||||
<title>{{t .Lang "访问被禁止"}} - MailGo</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -43,34 +43,34 @@
|
||||
<body>
|
||||
<div class="banned-card">
|
||||
<div class="banned-icon">🚫</div>
|
||||
<h1>您的 IP 已被暂时禁止访问</h1>
|
||||
<p>由于登录失败次数过多,您的 IP 地址已被暂时封禁。</p>
|
||||
<h1>{{t .Lang "您的 IP 已被暂时禁止访问"}}</h1>
|
||||
<p>{{t .Lang "由于登录失败次数过多,您的 IP 地址已被暂时封禁。"}}</p>
|
||||
{{if .entry}}
|
||||
<div class="detail">
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">IP 地址</span>
|
||||
<span class="detail-label">{{t .Lang "IP 地址"}}</span>
|
||||
<span class="detail-value">{{.entry.IPAddress}}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">封禁档位</span>
|
||||
<span class="detail-value">{{if gt .entry.BanCount 3}}第 {{sub .entry.BanCount 3}} 次封禁{{else}}—{{end}}</span>
|
||||
<span class="detail-label">{{t .Lang "封禁档位"}}</span>
|
||||
<span class="detail-value">{{if gt .entry.BanCount 3}}{{tf .Lang "第 %d 次封禁" (sub .entry.BanCount 3)}}{{else}}—{{end}}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">原因</span>
|
||||
<span class="detail-value">{{.entry.Reason}}</span>
|
||||
<span class="detail-label">{{t .Lang "原因"}}</span>
|
||||
<span class="detail-value">{{t .Lang .entry.Reason}}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">失败次数</span>
|
||||
<span class="detail-value">{{.entry.FailCount}} 次</span>
|
||||
<span class="detail-label">{{t .Lang "失败次数"}}</span>
|
||||
<span class="detail-value">{{tf .Lang "%d 次" .entry.FailCount}}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">解封时间</span>
|
||||
<span class="detail-value">{{time12 .entry.ExpiresAt}}</span>
|
||||
<span class="detail-label">{{t .Lang "解封时间"}}</span>
|
||||
<span class="detail-value">{{time12 .Lang .entry.ExpiresAt}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<p style="font-size:13px;">请在解封时间后重试,或联系管理员。</p>
|
||||
<a href="/login" class="back-link">返回登录页</a>
|
||||
<p style="font-size:13px;">{{t .Lang "请在解封时间后重试,或联系管理员。"}}</p>
|
||||
<a href="/login" class="back-link">{{t .Lang "返回登录页"}}</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -433,22 +433,22 @@
|
||||
<div class="topbar-left">
|
||||
<a class="logo" href="/inbox">
|
||||
<span class="logo-icon">✉</span>
|
||||
<span class="logo-text">MailGo<em>邮箱</em></span>
|
||||
<span class="logo-text">MailGo<em>{{t .Lang "邮箱"}}</em></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="topbar-search">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<input id="mail-search" type="text" placeholder="搜索邮件(发件人 / 主题)" autocomplete="off">
|
||||
<input id="mail-search" type="text" placeholder="{{t .Lang "搜索邮件(发件人 / 主题)"}}" autocomplete="off">
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
{{if .currentUser.IsAdmin}}<a class="topbar-link" href="/admin">管理后台</a>{{end}}
|
||||
<a class="topbar-link" href="/settings">设置</a>
|
||||
{{if .currentUser.IsAdmin}}<a class="topbar-link" href="/admin">{{t .Lang "管理后台"}}</a>{{end}}
|
||||
<a class="topbar-link" href="/settings">{{t .Lang "设置"}}</a>
|
||||
<span class="user-chip">
|
||||
<span class="avatar avatar-sm" style="{{avatarStyle .currentUser.Username}}">{{initial .currentUser.Username}}</span>
|
||||
<span class="user-email" title="{{.currentUser.Username}}@{{.currentUser.Domain.Name}}">{{.currentUser.Username}}@{{.currentUser.Domain.Name}}</span>
|
||||
</span>
|
||||
<form method="POST" action="/logout" class="logout-form">
|
||||
<button type="submit" class="icon-btn" title="退出登录">
|
||||
<button type="submit" class="icon-btn" title="{{t .Lang "退出登录"}}">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
@@ -461,7 +461,7 @@
|
||||
<aside class="mail-sidebar">
|
||||
<a href="/compose" class="compose-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
写信
|
||||
{{t .Lang "写信"}}
|
||||
</a>
|
||||
<nav class="folder-nav">
|
||||
{{range .folders}}
|
||||
@@ -477,7 +477,7 @@
|
||||
{{else}}
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
||||
{{end}}
|
||||
{{folderLabel .Name}}
|
||||
{{folderLabel $.Lang .Name}}
|
||||
{{if eq .Name "INBOX"}}
|
||||
{{if .Unseen}}<span class="badge">{{.Unseen}}</span>{{end}}
|
||||
{{else if .Total}}<span class="count">{{.Total}}</span>{{end}}
|
||||
@@ -488,7 +488,7 @@
|
||||
{{if .currentUser.IsAdmin}}
|
||||
<a class="folder" href="/admin">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||||
管理后台
|
||||
{{t .Lang "管理后台"}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
@@ -541,7 +541,7 @@
|
||||
var ids = rows.filter(function (r) { return r.isConnected && r.querySelector('.row-check').checked; })
|
||||
.map(function (r) { return r.dataset.id; });
|
||||
if (!ids.length) return;
|
||||
if (!confirm('确定要删除选中的 ' + ids.length + ' 封邮件吗?')) return;
|
||||
if (!confirm('{{t .Lang "确定要删除选中的 %s 封邮件吗?"}}'.replace('%s', ids.length))) return;
|
||||
var done = 0;
|
||||
ids.forEach(function (id) {
|
||||
fetch('/mail/delete/' + id, { method: 'POST', body: new FormData() })
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "compose"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>写信 - MailGo</title>
|
||||
<title>{{t .Lang "写信"}} - MailGo</title>
|
||||
<link href="/static/vendor/quill/quill.snow.css" rel="stylesheet">
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
@@ -16,44 +16,44 @@
|
||||
<div class="compose-toolbar">
|
||||
<button type="submit" form="compose-form" class="btn btn-primary">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
|
||||
发送
|
||||
{{t .Lang "发送"}}
|
||||
</button>
|
||||
<label class="tb-btn" style="cursor:pointer;">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
|
||||
附件
|
||||
{{t .Lang "附件"}}
|
||||
<input type="file" name="attachments" id="attach-input" multiple style="display:none;">
|
||||
</label>
|
||||
<a href="/inbox" class="tb-btn">取消</a>
|
||||
<a href="/inbox" class="tb-btn">{{t .Lang "取消"}}</a>
|
||||
<div class="toolbar-spacer"></div>
|
||||
<span class="page-info">撰写新邮件</span>
|
||||
<span class="page-info">{{t .Lang "撰写新邮件"}}</span>
|
||||
</div>
|
||||
|
||||
{{if .error}}<div class="alert alert-error" style="margin:12px 18px 0;">{{.error}}</div>{{end}}
|
||||
|
||||
<form id="compose-form" class="compose-form" method="POST" action="/compose" enctype="multipart/form-data">
|
||||
<div class="compose-field">
|
||||
<label>收件人</label>
|
||||
<input type="email" name="to" required value="{{.to}}" placeholder="输入收件人邮箱地址">
|
||||
<label>{{t .Lang "收件人"}}</label>
|
||||
<input type="email" name="to" required value="{{.to}}" placeholder="{{t .Lang "输入收件人邮箱地址"}}">
|
||||
</div>
|
||||
<div class="compose-field">
|
||||
<label>抄送</label>
|
||||
<input type="text" name="cc" value="{{.cc}}" placeholder="多个地址用逗号分隔(可选)">
|
||||
<label>{{t .Lang "抄送"}}</label>
|
||||
<input type="text" name="cc" value="{{.cc}}" placeholder="{{t .Lang "多个地址用逗号分隔(可选)"}}">
|
||||
</div>
|
||||
<div class="compose-field">
|
||||
<label>主题</label>
|
||||
<input type="text" name="subject" value="{{.subject}}" placeholder="输入邮件主题">
|
||||
<label>{{t .Lang "主题"}}</label>
|
||||
<input type="text" name="subject" value="{{.subject}}" placeholder="{{t .Lang "输入邮件主题"}}">
|
||||
</div>
|
||||
|
||||
<div id="attach-chips" class="attach-chips" style="{{if not .attachments}}display:none;{{end}}"></div>
|
||||
|
||||
<div class="editor-wrap">
|
||||
<div id="editor" data-placeholder="请输入邮件内容..."></div>
|
||||
<div id="editor" data-placeholder="{{t .Lang "请输入邮件内容..."}}"></div>
|
||||
<input type="hidden" name="body" id="body-hidden">
|
||||
<input type="hidden" name="html_body" id="html-body-hidden">
|
||||
</div>
|
||||
|
||||
<div class="compose-footer">
|
||||
<span>附件配额</span>
|
||||
<span>{{t .Lang "附件配额"}}</span>
|
||||
<span class="quota-bar" id="quota-bar" data-used="{{.usedBytes}}" data-quota="{{.quotaBytes}}"><i></i></span>
|
||||
<span id="quota-text">{{formatBytes .usedBytes}} / {{formatBytes .quotaBytes}}</span>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@
|
||||
<script>
|
||||
var quill = new Quill('#editor', {
|
||||
theme: 'snow',
|
||||
placeholder: document.getElementById('editor').dataset.placeholder || '请输入邮件内容...',
|
||||
placeholder: document.getElementById('editor').dataset.placeholder || '{{t .Lang "请输入邮件内容..."}}',
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ 'header': [1, 2, 3, false] }],
|
||||
@@ -96,7 +96,7 @@
|
||||
var chip = document.createElement('span');
|
||||
chip.className = 'attach-chip';
|
||||
chip.innerHTML = '📎 ' + f.name + ' (' + (f.size / 1024).toFixed(1) + ' KB)' +
|
||||
'<button type="button" class="chip-del" data-i="' + i + '" title="移除">✕</button>';
|
||||
'<button type="button" class="chip-del" data-i="' + i + '" title="{{t .Lang "移除"}}">✕</button>';
|
||||
chipsBox.appendChild(chip);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "folder"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>{{folderLabel .folder}} - MailGo</title>
|
||||
<title>{{folderLabel .Lang .folder}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body class="page-list">
|
||||
@@ -13,31 +13,31 @@
|
||||
{{template "sidebar" .}}
|
||||
<main class="mail-main">
|
||||
<div class="list-toolbar">
|
||||
<label class="check-all" title="全选/取消全选">
|
||||
<label class="check-all" title="{{t .Lang "全选/取消全选"}}">
|
||||
<input type="checkbox" id="select-all">
|
||||
全选
|
||||
{{t .Lang "全选"}}
|
||||
</label>
|
||||
<button type="button" class="tb-btn" id="btn-refresh" title="刷新">
|
||||
<button type="button" class="tb-btn" id="btn-refresh" title="{{t .Lang "刷新"}}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
|
||||
刷新
|
||||
{{t .Lang "刷新"}}
|
||||
</button>
|
||||
<button type="button" class="tb-btn danger" id="btn-delete" disabled>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
{{if .isTrash}}彻底删除{{else}}删除{{end}}
|
||||
{{if .isTrash}}{{t $.Lang "彻底删除"}}{{else}}{{t $.Lang "删除"}}{{end}}
|
||||
</button>
|
||||
{{if .isTrash}}
|
||||
<form method="POST" action="/folder/Trash/empty" style="display:inline;"
|
||||
onsubmit="return confirm('确定要清空「已删除」文件夹吗?此操作不可恢复。');">
|
||||
<button type="submit" class="tb-btn danger">清空文件夹</button>
|
||||
onsubmit="return confirm('{{t $.Lang "确定要清空「已删除」文件夹吗?此操作不可恢复。"}}');">
|
||||
<button type="submit" class="tb-btn danger">{{t $.Lang "清空文件夹"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
<div class="toolbar-spacer"></div>
|
||||
<span class="page-info">共 {{.total}} 封</span>
|
||||
<span class="page-info">{{tf .Lang "共 %d 封" .total}}</span>
|
||||
</div>
|
||||
|
||||
{{if not .messages}}
|
||||
<div class="mail-list">
|
||||
<div class="empty-tip"><span class="empty-icon">{{if .isTrash}}🗑{{else}}📭{{end}}</span>{{folderLabel .folder}}暂无邮件</div>
|
||||
<div class="empty-tip"><span class="empty-icon">{{if .isTrash}}🗑{{else}}📭{{end}}</span>{{folderLabel $.Lang $.folder}} · {{t $.Lang "暂无邮件"}}</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<ul class="mail-list">
|
||||
@@ -53,9 +53,9 @@
|
||||
<span class="avatar" style="{{avatarStyle .FromAddr}}">{{initial (mailName (decodeHeader .FromAddr))}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
<span class="cell-from" {{if or (eq $.folder "Sent") (eq $.folder "Drafts")}}title="收件人:{{.ToAddr}}"{{else}}title="{{decodeHeader .FromAddr}}"{{end}}>
|
||||
<span class="cell-from" {{if or (eq $.folder "Sent") (eq $.folder "Drafts")}}title="{{tf $.Lang "收件人:%s" .ToAddr}}"{{else}}title="{{decodeHeader .FromAddr}}"{{end}}>
|
||||
{{if or (eq $.folder "Sent") (eq $.folder "Drafts")}}
|
||||
{{if eq $.folder "Drafts"}}致:{{end}}{{mailName .ToAddr}}
|
||||
{{if eq $.folder "Drafts"}}{{t $.Lang "致:"}}{{end}}{{mailName .ToAddr}}
|
||||
{{else}}
|
||||
{{mailName (decodeHeader .FromAddr)}}
|
||||
{{end}}
|
||||
@@ -63,24 +63,24 @@
|
||||
<span class="cell-subject-wrap">
|
||||
{{if not .IsRead}}<span class="unread-dot"></span>{{end}}
|
||||
<a class="cell-subject" href="/folder/{{urlPath $.folder}}/{{.ID}}">
|
||||
{{if .Subject}}{{.Subject}}{{else}}(无主题){{end}}
|
||||
{{if .Subject}}{{.Subject}}{{else}}{{t $.Lang "(无主题)"}}{{end}}
|
||||
</a>
|
||||
</span>
|
||||
<span class="cell-snippet">
|
||||
{{if .TextBody}}{{truncate .TextBody 80}}{{else if .HtmlBody}}[HTML 邮件]{{end}}
|
||||
{{if .TextBody}}{{truncate .TextBody 80}}{{else if .HtmlBody}}{{t $.Lang "[HTML 邮件]"}}{{end}}
|
||||
</span>
|
||||
<span class="cell-date">{{shortDate .Date}}</span>
|
||||
<span class="cell-date">{{shortDate $.Lang .Date}}</span>
|
||||
{{if $.isTrash}}
|
||||
<form method="POST" action="/mail/restore/{{.ID}}" class="row-del"
|
||||
onsubmit="event.stopPropagation(); return confirm('确定要恢复这封邮件吗?');">
|
||||
<button type="submit" class="icon-btn" title="恢复到收件箱">
|
||||
onsubmit="event.stopPropagation(); return confirm('{{t $.Lang "确定要恢复这封邮件吗?"}}');">
|
||||
<button type="submit" class="icon-btn" title="{{t $.Lang "恢复到收件箱"}}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<form method="POST" action="/mail/delete/{{.ID}}" class="row-del"
|
||||
onsubmit="event.stopPropagation(); return confirm('确定要删除这封邮件吗?');">
|
||||
<button type="submit" class="icon-btn" title="删除">
|
||||
onsubmit="event.stopPropagation(); return confirm('{{t $.Lang "确定要删除这封邮件吗?"}}');">
|
||||
<button type="submit" class="icon-btn" title="{{t $.Lang "删除"}}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
@@ -91,23 +91,23 @@
|
||||
{{end}}
|
||||
|
||||
<div class="list-footer">
|
||||
<span class="page-num">第 {{.page}} / {{if .totalPages}}{{.totalPages}}{{else}}1{{end}} 页</span>
|
||||
<span class="page-num">{{tf .Lang "第 %d / %d 页" .page (or .totalPages 1)}}</span>
|
||||
<div class="pager">
|
||||
{{if gt .page 1}}
|
||||
<a class="page-btn" href="/folder/{{urlPath .folder}}?page={{sub .page 1}}">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
|
||||
上一页
|
||||
{{t $.Lang "上一页"}}
|
||||
</a>
|
||||
{{else}}
|
||||
<span class="page-btn disabled">上一页</span>
|
||||
<span class="page-btn disabled">{{t $.Lang "上一页"}}</span>
|
||||
{{end}}
|
||||
{{if lt .page .totalPages}}
|
||||
<a class="page-btn" href="/folder/{{urlPath .folder}}?page={{add .page 1}}">
|
||||
下一页
|
||||
{{t $.Lang "下一页"}}
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</a>
|
||||
{{else}}
|
||||
<span class="page-btn disabled">下一页</span>
|
||||
<span class="page-btn disabled">{{t $.Lang "下一页"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
{{define "login"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>登录 - MailGo 邮箱</title>
|
||||
<title>{{t .Lang "登录"}} - MailGo {{t .Lang "邮箱"}}</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<div class="login-card">
|
||||
<div class="login-logo">
|
||||
<span class="logo-icon" style="width:44px;height:44px;font-size:22px;border-radius:12px;">✉</span>
|
||||
<span style="font-size:26px;font-weight:700;color:var(--primary);">MailGo<em style="font-style:normal;font-weight:400;font-size:15px;color:var(--text-2);margin-left:4px;">邮箱</em></span>
|
||||
<span style="font-size:26px;font-weight:700;color:var(--primary);">MailGo<em style="font-style:normal;font-weight:400;font-size:15px;color:var(--text-2);margin-left:4px;">{{t .Lang "邮箱"}}</em></span>
|
||||
</div>
|
||||
<div class="login-sub">登录您的邮箱账户</div>
|
||||
<div class="login-sub">{{t .Lang "登录您的邮箱账户"}}</div>
|
||||
|
||||
{{if .error}}<div class="alert alert-error">{{.error}}</div>{{end}}
|
||||
|
||||
<form method="POST" action="/login">
|
||||
<div class="form-group">
|
||||
<label>邮箱地址</label>
|
||||
<label>{{t .Lang "邮箱地址"}}</label>
|
||||
<input type="email" name="email" required autofocus placeholder="user@example.com" style="padding:11px 14px;border-radius:8px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" name="password" required placeholder="请输入密码" style="padding:11px 14px;border-radius:8px;">
|
||||
<label>{{t .Lang "密码"}}</label>
|
||||
<input type="password" name="password" required placeholder="{{t .Lang "请输入密码"}}" style="padding:11px 14px;border-radius:8px;">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width:100%;padding:11px 16px;font-size:15px;border-radius:8px;">登 录</button>
|
||||
<button type="submit" class="btn btn-primary" style="width:100%;padding:11px 16px;font-size:15px;border-radius:8px;">{{t .Lang "登 录"}}</button>
|
||||
</form>
|
||||
|
||||
{{if or .oauth2Enabled .ldapEnabled}}
|
||||
<div class="divider">或</div>
|
||||
<div class="divider">{{t .Lang "或"}}</div>
|
||||
{{end}}
|
||||
|
||||
{{if .ldapEnabled}}
|
||||
<form method="POST" action="/login/ldap" style="margin-bottom:10px;">
|
||||
<div class="form-group">
|
||||
<label>LDAP 用户名</label>
|
||||
<input type="text" name="username" placeholder="LDAP 用户名">
|
||||
<label>{{t .Lang "LDAP 用户名"}}</label>
|
||||
<input type="text" name="username" placeholder="{{t .Lang "LDAP 用户名"}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>LDAP 密码</label>
|
||||
<input type="password" name="password" placeholder="LDAP 密码">
|
||||
<label>{{t .Lang "LDAP 密码"}}</label>
|
||||
<input type="password" name="password" placeholder="{{t .Lang "LDAP 密码"}}">
|
||||
</div>
|
||||
<button type="submit" class="btn" style="width:100%;background:#7b5cd6;color:#fff;">LDAP 登录</button>
|
||||
<button type="submit" class="btn" style="width:100%;background:#7b5cd6;color:#fff;">{{t .Lang "LDAP 登录"}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
|
||||
{{if .oauth2Enabled}}
|
||||
<a href="/auth/oauth2" class="btn btn-primary" style="width:100%;text-align:center;display:block;margin-top:4px;">
|
||||
{{if eq .oauth2Provider "google"}}Google{{else if eq .oauth2Provider "github"}}GitHub{{else}}OAuth2{{end}} 登录
|
||||
{{if eq .oauth2Provider "google"}}Google{{else if eq .oauth2Provider "github"}}GitHub{{else}}OAuth2{{end}} {{t .Lang "登录"}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "settings"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>设置 - MailGo</title>
|
||||
<title>{{t .Lang "设置"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body class="page-settings">
|
||||
@@ -15,21 +15,21 @@
|
||||
{{if .error}}<div class="alert alert-error">{{.error}}</div>{{end}}
|
||||
{{if .success}}<div class="alert alert-success">{{.success}}</div>{{end}}
|
||||
{{if .mustChange}}<div class="alert" style="border:1px solid #ffa940;background:#fff7e6;color:#d46b08;border-radius:8px;padding:12px 16px;margin-bottom:16px;font-size:13.5px;">
|
||||
⚠️ 首次登录/密码已被重置,请立即修改密码后再继续使用邮箱功能。
|
||||
{{t .Lang "⚠️ 首次登录/密码已被重置,请立即修改密码后再继续使用邮箱功能。"}}
|
||||
</div>{{end}}
|
||||
|
||||
<div class="card" style="max-width:720px;">
|
||||
<h2 style="font-size:17px;margin-bottom:18px;display:flex;align-items:center;gap:10px;">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#1677ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
账号信息
|
||||
{{t .Lang "账号信息"}}
|
||||
</h2>
|
||||
<div style="display:flex;align-items:center;gap:14px;margin-bottom:20px;">
|
||||
<span class="avatar" style="width:52px;height:52px;font-size:22px;{{avatarStyle .currentUser.Username}}">{{initial .currentUser.Username}}</span>
|
||||
<div>
|
||||
<div style="font-size:16px;font-weight:600;">{{.currentUser.Username}}@{{.currentUser.Domain.Name}}</div>
|
||||
<div style="color:var(--text-3);font-size:12.5px;margin-top:3px;">
|
||||
已用 {{formatBytes .currentUser.UsedBytes}} / 配额 {{formatBytes .currentUser.QuotaBytes}}
|
||||
{{if .currentUser.IsAdmin}} · 管理员{{end}}
|
||||
{{tf .Lang "已用 %s / 配额 %s" (formatBytes .currentUser.UsedBytes) (formatBytes .currentUser.QuotaBytes)}}
|
||||
{{if .currentUser.IsAdmin}} · {{t .Lang "管理员"}}{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,30 +37,46 @@
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width:720px;">
|
||||
<h2 style="font-size:17px;margin-bottom:18px;">修改密码</h2>
|
||||
<h2 style="font-size:17px;margin-bottom:18px;">{{t .Lang "界面语言"}}</h2>
|
||||
<form method="POST" action="/settings" style="max-width:420px;">
|
||||
<div class="form-group">
|
||||
<label>当前密码</label>
|
||||
<input type="password" name="old_password" required placeholder="请输入当前密码" autocomplete="current-password">
|
||||
<label>{{t .Lang "界面语言"}}</label>
|
||||
<select name="language">
|
||||
<option value="auto" {{if eq .currentUser.Language "auto"}}selected{{end}}>{{t .Lang "自动(跟随浏览器)"}}</option>
|
||||
<option value="en" {{if eq .currentUser.Language "en"}}selected{{end}}>English</option>
|
||||
<option value="zh" {{if eq .currentUser.Language "zh"}}selected{{end}}>中文</option>
|
||||
<option value="ja" {{if eq .currentUser.Language "ja"}}selected{{end}}>日本語</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>新密码</label>
|
||||
<input type="password" name="new_password" required placeholder="请输入新密码" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>确认新密码</label>
|
||||
<input type="password" name="confirm_password" required placeholder="请再次输入新密码" autocomplete="new-password">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">修改密码</button>
|
||||
<button type="submit" class="btn btn-primary">{{t .Lang "保存"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width:720px;">
|
||||
<h2 style="font-size:17px;margin-bottom:10px;">帮助</h2>
|
||||
<h2 style="font-size:17px;margin-bottom:18px;">{{t .Lang "修改密码"}}</h2>
|
||||
<form method="POST" action="/settings" style="max-width:420px;">
|
||||
<div class="form-group">
|
||||
<label>{{t .Lang "当前密码"}}</label>
|
||||
<input type="password" name="old_password" required placeholder="{{t .Lang "请输入当前密码"}}" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{t .Lang "新密码"}}</label>
|
||||
<input type="password" name="new_password" required placeholder="{{t .Lang "请输入新密码"}}" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{t .Lang "确认新密码"}}</label>
|
||||
<input type="password" name="confirm_password" required placeholder="{{t .Lang "请再次输入新密码"}}" autocomplete="new-password">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">{{t .Lang "修改密码"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width:720px;">
|
||||
<h2 style="font-size:17px;margin-bottom:10px;">{{t .Lang "帮助"}}</h2>
|
||||
<p style="color:var(--text-2);font-size:13.5px;line-height:1.9;">
|
||||
客户端收发信(IMAP / SMTP)配置:<br>
|
||||
IMAP 服务器:{{.currentUser.Domain.Name}} 端口 143 / SSL 993<br>
|
||||
SMTP 服务器:{{.currentUser.Domain.Name}} 端口 587(提交)/ SSL 465
|
||||
{{t .Lang "客户端收发信(IMAP / SMTP)配置:"}}<br>
|
||||
{{tf .Lang "IMAP 服务器:%s 端口 %d / SSL %d" .currentUser.Domain.Name 143 993}}<br>
|
||||
{{tf .Lang "SMTP 服务器:%s 端口 %d(提交)/ SSL %d" .currentUser.Domain.Name 587 465}}
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "view"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="{{htmlLang .Lang}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>查看邮件 - MailGo</title>
|
||||
<title>{{t .Lang "查看邮件"}} - MailGo</title>
|
||||
{{template "styles" .}}
|
||||
</head>
|
||||
<body class="page-view">
|
||||
@@ -15,50 +15,50 @@
|
||||
<div class="view-toolbar">
|
||||
<a href="javascript:history.back()" class="tb-btn">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
返回
|
||||
{{t .Lang "返回"}}
|
||||
</a>
|
||||
<a href="/compose?to={{mailEmail .message.FromAddr}}&subject={{if .message.Subject}}Re: {{.message.Subject}}{{end}}" class="tb-btn">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>
|
||||
回复
|
||||
{{t .Lang "回复"}}
|
||||
</a>
|
||||
{{if .inTrash}}
|
||||
<form method="POST" action="/mail/restore/{{.message.ID}}" style="display:inline;">
|
||||
<button type="submit" class="tb-btn">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
|
||||
恢复
|
||||
{{t .Lang "恢复"}}
|
||||
</button>
|
||||
</form>
|
||||
<form method="POST" action="/mail/purge/{{.message.ID}}" style="display:inline;"
|
||||
onsubmit="return confirm('确定要彻底删除这封邮件吗?此操作不可恢复。');">
|
||||
onsubmit="return confirm('{{t .Lang "确定要彻底删除这封邮件吗?此操作不可恢复。"}}');">
|
||||
<button type="submit" class="tb-btn danger">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
彻底删除
|
||||
{{t .Lang "彻底删除"}}
|
||||
</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<form method="POST" action="/mail/delete/{{.message.ID}}" style="display:inline;"
|
||||
onsubmit="return confirm('确定要删除这封邮件吗?');">
|
||||
onsubmit="return confirm('{{t .Lang "确定要删除这封邮件吗?"}}');">
|
||||
<button type="submit" class="tb-btn danger">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
删除
|
||||
{{t .Lang "删除"}}
|
||||
</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="mail-head">
|
||||
<h1 class="mail-title">{{if .message.Subject}}{{.message.Subject}}{{else}}(无主题){{end}}</h1>
|
||||
<h1 class="mail-title">{{if .message.Subject}}{{.message.Subject}}{{else}}{{t .Lang "(无主题)"}}{{end}}</h1>
|
||||
<div class="mail-from-row">
|
||||
<span class="avatar" style="{{avatarStyle .message.FromAddr}}">{{initial (mailName (decodeHeader .message.FromAddr))}}</span>
|
||||
<div>
|
||||
<div class="mail-from-name">{{mailName (decodeHeader .message.FromAddr)}}</div>
|
||||
<div class="mail-from-addr" title="{{decodeHeader .message.FromAddr}}">{{mailEmail .message.FromAddr}}</div>
|
||||
</div>
|
||||
<span class="mail-date">{{time12 .message.Date}}</span>
|
||||
<span class="mail-date">{{time12 .Lang .message.Date}}</span>
|
||||
</div>
|
||||
{{if .message.CcAddr}}
|
||||
<div class="mail-from-addr" style="margin:-8px 0 16px 48px;">
|
||||
抄送:{{.message.CcAddr}}
|
||||
{{t .Lang "抄送:"}}{{.message.CcAddr}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
@@ -88,28 +88,28 @@
|
||||
<div class="view-actions">
|
||||
<a href="/compose?to={{mailEmail .message.FromAddr}}&subject={{if .message.Subject}}Re: {{.message.Subject}}{{end}}" class="btn btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>
|
||||
回复
|
||||
{{t .Lang "回复"}}
|
||||
</a>
|
||||
{{if .inTrash}}
|
||||
<form method="POST" action="/mail/restore/{{.message.ID}}" style="display:inline;">
|
||||
<button type="submit" class="btn">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
|
||||
恢复到收件箱
|
||||
{{t .Lang "恢复到收件箱"}}
|
||||
</button>
|
||||
</form>
|
||||
<form method="POST" action="/mail/purge/{{.message.ID}}" style="display:inline;"
|
||||
onsubmit="return confirm('确定要彻底删除这封邮件吗?此操作不可恢复。');">
|
||||
onsubmit="return confirm('{{t .Lang "确定要彻底删除这封邮件吗?此操作不可恢复。"}}');">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
彻底删除
|
||||
{{t .Lang "彻底删除"}}
|
||||
</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<form method="POST" action="/mail/delete/{{.message.ID}}" style="display:inline;"
|
||||
onsubmit="return confirm('确定要删除这封邮件吗?');">
|
||||
onsubmit="return confirm('{{t .Lang "确定要删除这封邮件吗?"}}');">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
删除邮件
|
||||
{{t .Lang "删除邮件"}}
|
||||
</button>
|
||||
</form>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user