tempmail_sdk 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +116 -0
- data/lib/tempmail_sdk/channels.rb +289 -0
- data/lib/tempmail_sdk/channels_data.rb +289 -0
- data/lib/tempmail_sdk/client.rb +193 -0
- data/lib/tempmail_sdk/config.rb +104 -0
- data/lib/tempmail_sdk/html_utils.rb +42 -0
- data/lib/tempmail_sdk/http.rb +225 -0
- data/lib/tempmail_sdk/logger.rb +38 -0
- data/lib/tempmail_sdk/normalize.rb +193 -0
- data/lib/tempmail_sdk/providers/anonbox.rb +248 -0
- data/lib/tempmail_sdk/providers/anonymmail.rb +92 -0
- data/lib/tempmail_sdk/providers/byom.rb +48 -0
- data/lib/tempmail_sdk/providers/catchmail.rb +110 -0
- data/lib/tempmail_sdk/providers/cleantempmail.rb +49 -0
- data/lib/tempmail_sdk/providers/devmail_uk.rb +58 -0
- data/lib/tempmail_sdk/providers/dropmail.rb +133 -0
- data/lib/tempmail_sdk/providers/dropmail_click.rb +81 -0
- data/lib/tempmail_sdk/providers/dropmail_me.rb +163 -0
- data/lib/tempmail_sdk/providers/duckmail.rb +63 -0
- data/lib/tempmail_sdk/providers/eyepaste.rb +119 -0
- data/lib/tempmail_sdk/providers/fake_email_site.rb +48 -0
- data/lib/tempmail_sdk/providers/fake_legal.rb +109 -0
- data/lib/tempmail_sdk/providers/fmail.rb +87 -0
- data/lib/tempmail_sdk/providers/freecustom.rb +135 -0
- data/lib/tempmail_sdk/providers/getnada.rb +146 -0
- data/lib/tempmail_sdk/providers/gonebox_email.rb +89 -0
- data/lib/tempmail_sdk/providers/guerrillamail.rb +62 -0
- data/lib/tempmail_sdk/providers/guerrillamail_mirrors.rb +81 -0
- data/lib/tempmail_sdk/providers/harakirimail.rb +84 -0
- data/lib/tempmail_sdk/providers/haribu.rb +176 -0
- data/lib/tempmail_sdk/providers/inboxes.rb +110 -0
- data/lib/tempmail_sdk/providers/inboxkitten.rb +90 -0
- data/lib/tempmail_sdk/providers/linshiyou.rb +157 -0
- data/lib/tempmail_sdk/providers/lroid.rb +170 -0
- data/lib/tempmail_sdk/providers/m2u.rb +126 -0
- data/lib/tempmail_sdk/providers/mail10s.rb +59 -0
- data/lib/tempmail_sdk/providers/mail123.rb +82 -0
- data/lib/tempmail_sdk/providers/mail_cx.rb +133 -0
- data/lib/tempmail_sdk/providers/mail_sunls.rb +68 -0
- data/lib/tempmail_sdk/providers/mail_tm.rb +99 -0
- data/lib/tempmail_sdk/providers/mailcatch.rb +91 -0
- data/lib/tempmail_sdk/providers/maildrop.rb +92 -0
- data/lib/tempmail_sdk/providers/maildrop_cc.rb +110 -0
- data/lib/tempmail_sdk/providers/mailforspam.rb +96 -0
- data/lib/tempmail_sdk/providers/mailhole_de.rb +70 -0
- data/lib/tempmail_sdk/providers/mailinator.rb +182 -0
- data/lib/tempmail_sdk/providers/mailmomy.rb +108 -0
- data/lib/tempmail_sdk/providers/mailnesia.rb +141 -0
- data/lib/tempmail_sdk/providers/mffac.rb +101 -0
- data/lib/tempmail_sdk/providers/moakt.rb +296 -0
- data/lib/tempmail_sdk/providers/neighbours.rb +140 -0
- data/lib/tempmail_sdk/providers/neighbours_sh.rb +117 -0
- data/lib/tempmail_sdk/providers/nimail.rb +100 -0
- data/lib/tempmail_sdk/providers/ockito.rb +163 -0
- data/lib/tempmail_sdk/providers/one_sec_mail.rb +46 -0
- data/lib/tempmail_sdk/providers/restmail_net.rb +60 -0
- data/lib/tempmail_sdk/providers/rootsh.rb +167 -0
- data/lib/tempmail_sdk/providers/shitty_email.rb +84 -0
- data/lib/tempmail_sdk/providers/smailpro.rb +103 -0
- data/lib/tempmail_sdk/providers/ta_easy.rb +53 -0
- data/lib/tempmail_sdk/providers/temp_mail_io.rb +67 -0
- data/lib/tempmail_sdk/providers/temp_mail_now.rb +103 -0
- data/lib/tempmail_sdk/providers/temp_mail_org.rb +89 -0
- data/lib/tempmail_sdk/providers/tempemail_co.rb +142 -0
- data/lib/tempmail_sdk/providers/tempemail_info.rb +121 -0
- data/lib/tempmail_sdk/providers/tempemails_net.rb +159 -0
- data/lib/tempmail_sdk/providers/tempfastmail.rb +76 -0
- data/lib/tempmail_sdk/providers/tempgmailer.rb +126 -0
- data/lib/tempmail_sdk/providers/tempgo_email.rb +74 -0
- data/lib/tempmail_sdk/providers/tempinbox.rb +70 -0
- data/lib/tempmail_sdk/providers/tempmail.rb +66 -0
- data/lib/tempmail_sdk/providers/tempmail365.rb +119 -0
- data/lib/tempmail_sdk/providers/tempmail_fish.rb +89 -0
- data/lib/tempmail_sdk/providers/tempmail_fyi.rb +124 -0
- data/lib/tempmail_sdk/providers/tempmail_lol.rb +42 -0
- data/lib/tempmail_sdk/providers/tempmail_lol_v2.rb +57 -0
- data/lib/tempmail_sdk/providers/tempmail_plus.rb +80 -0
- data/lib/tempmail_sdk/providers/tempmailc.rb +80 -0
- data/lib/tempmail_sdk/providers/tempmailpro.rb +89 -0
- data/lib/tempmail_sdk/providers/tempmailten.rb +150 -0
- data/lib/tempmail_sdk/providers/temporam.rb +95 -0
- data/lib/tempmail_sdk/providers/tempp_mails.rb +150 -0
- data/lib/tempmail_sdk/providers/tempy_email.rb +73 -0
- data/lib/tempmail_sdk/providers/ten_minute_mail_net.rb +125 -0
- data/lib/tempmail_sdk/providers/tenminute_one.rb +255 -0
- data/lib/tempmail_sdk/providers/throwawaymail.rb +80 -0
- data/lib/tempmail_sdk/providers/tmail_link.rb +98 -0
- data/lib/tempmail_sdk/providers/uncorreotemporal.rb +84 -0
- data/lib/tempmail_sdk/providers/webmailtemp.rb +108 -0
- data/lib/tempmail_sdk/providers/xkx_me.rb +125 -0
- data/lib/tempmail_sdk/providers/zhujump.rb +256 -0
- data/lib/tempmail_sdk/registry.rb +1097 -0
- data/lib/tempmail_sdk/retry.rb +91 -0
- data/lib/tempmail_sdk/telemetry.rb +134 -0
- data/lib/tempmail_sdk/types.rb +164 -0
- data/lib/tempmail_sdk/version.rb +6 -0
- data/lib/tempmail_sdk/webui.rb +360 -0
- data/lib/tempmail_sdk.rb +76 -0
- metadata +142 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "thread"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module TempmailSdk
|
|
8
|
+
# 嵌入式 WebUI HTTP 服务器
|
|
9
|
+
# 通过 TEMPMAIL_WEBUI=true/1/yes 激活,TEMPMAIL_WEBUI_PORT 指定端口(未设置则随机)
|
|
10
|
+
# 仅依赖 Ruby 标准库,不引入任何外部 gem
|
|
11
|
+
module WebUI
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# SSE 客户端队列列表与互斥锁
|
|
15
|
+
@sse_clients = []
|
|
16
|
+
@sse_mutex = Mutex.new
|
|
17
|
+
@server_mutex = Mutex.new
|
|
18
|
+
@server = nil
|
|
19
|
+
@port = nil
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
attr_reader :port, :host
|
|
23
|
+
|
|
24
|
+
# 启动 WebUI(幂等)
|
|
25
|
+
def start(host: nil, port: nil)
|
|
26
|
+
@server_mutex.synchronize do
|
|
27
|
+
return @port if @server
|
|
28
|
+
|
|
29
|
+
bind_host = host || _resolve_host
|
|
30
|
+
bind_port = port || _resolve_port
|
|
31
|
+
@server = TCPServer.new(bind_host, bind_port)
|
|
32
|
+
@host = bind_host
|
|
33
|
+
@port = @server.addr[1]
|
|
34
|
+
|
|
35
|
+
# 挂接日志处理器
|
|
36
|
+
_attach_log_handler
|
|
37
|
+
|
|
38
|
+
Thread.new { _serve }
|
|
39
|
+
Log.logger.info("WebUI 已启动: http://#{@host}:#{@port}")
|
|
40
|
+
@port
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# 广播日志条目到所有 SSE 连接
|
|
45
|
+
def broadcast(entry)
|
|
46
|
+
data = JSON.generate(entry)
|
|
47
|
+
@sse_mutex.synchronize { @sse_clients.dup }.each do |q|
|
|
48
|
+
q.push(data) rescue nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def _resolve_host
|
|
55
|
+
raw = (ENV["TEMPMAIL_WEBUI_HOST"] || "").strip
|
|
56
|
+
raw.empty? ? "127.0.0.1" : raw
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def _resolve_port
|
|
60
|
+
raw = (ENV["TEMPMAIL_WEBUI_PORT"] || "").strip
|
|
61
|
+
raw.match?(/\A\d+\z/) ? raw.to_i : 0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def _attach_log_handler
|
|
65
|
+
orig_formatter = Log.logger.formatter
|
|
66
|
+
Log.logger.formatter = proc do |severity, time, progname, msg|
|
|
67
|
+
entry = {
|
|
68
|
+
time: time.strftime("%H:%M:%S"),
|
|
69
|
+
level: _severity_to_level(severity),
|
|
70
|
+
channel: nil,
|
|
71
|
+
msg: msg.to_s
|
|
72
|
+
}
|
|
73
|
+
broadcast(entry)
|
|
74
|
+
orig_formatter ? orig_formatter.call(severity, time, progname, msg) : "#{severity} #{msg}\n"
|
|
75
|
+
end
|
|
76
|
+
# 确保 logger 级别足够低以捕获 debug
|
|
77
|
+
Log.logger.level = Logger::DEBUG if Log.logger.level > Logger::DEBUG
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def _severity_to_level(sev)
|
|
81
|
+
case sev.to_s.upcase
|
|
82
|
+
when "ERROR", "FATAL" then "error"
|
|
83
|
+
when "WARN" then "warn"
|
|
84
|
+
when "INFO" then "info"
|
|
85
|
+
else "debug"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def _serve
|
|
90
|
+
loop do
|
|
91
|
+
client = @server.accept rescue break
|
|
92
|
+
Thread.new(client) { |c| _handle(c) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def _handle(socket)
|
|
97
|
+
line = socket.gets&.chomp || ""
|
|
98
|
+
method, path, = line.split(" ")
|
|
99
|
+
# 消费请求头
|
|
100
|
+
loop do
|
|
101
|
+
h = socket.gets
|
|
102
|
+
break if h.nil? || h.chomp.empty?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
path = (path || "/").split("?", 2).first
|
|
106
|
+
|
|
107
|
+
case [method, path]
|
|
108
|
+
when ["GET", "/"]
|
|
109
|
+
_send_html(socket, _rendered_html)
|
|
110
|
+
when ["GET", "/api/info"]
|
|
111
|
+
_send_json(socket, _build_info)
|
|
112
|
+
when ["GET", "/api/channels"]
|
|
113
|
+
_send_json(socket, _build_channels)
|
|
114
|
+
when ["GET", "/api/logs/stream"]
|
|
115
|
+
_handle_sse(socket)
|
|
116
|
+
else
|
|
117
|
+
socket.write "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"
|
|
118
|
+
end
|
|
119
|
+
rescue StandardError
|
|
120
|
+
# 忽略单连接异常,不影响服务器主循环
|
|
121
|
+
ensure
|
|
122
|
+
socket.close rescue nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def _send_html(socket, html)
|
|
126
|
+
body = html.encode("UTF-8")
|
|
127
|
+
socket.write "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n" \
|
|
128
|
+
"Content-Length: #{body.bytesize}\r\nConnection: close\r\n\r\n#{body}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def _send_json(socket, payload)
|
|
132
|
+
body = JSON.generate(payload)
|
|
133
|
+
socket.write "HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\n" \
|
|
134
|
+
"Content-Length: #{body.bytesize}\r\nConnection: close\r\n\r\n#{body}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def _handle_sse(socket)
|
|
138
|
+
q = Queue.new
|
|
139
|
+
@sse_mutex.synchronize { @sse_clients << q }
|
|
140
|
+
socket.write "HTTP/1.1 200 OK\r\nContent-Type: text/event-stream; charset=utf-8\r\n" \
|
|
141
|
+
"Cache-Control: no-cache\r\nConnection: keep-alive\r\nX-Accel-Buffering: no\r\n\r\n"
|
|
142
|
+
socket.write ": connected\n\n"
|
|
143
|
+
socket.flush
|
|
144
|
+
loop do
|
|
145
|
+
data = q.pop
|
|
146
|
+
socket.write "data: #{data}\n\n"
|
|
147
|
+
socket.flush
|
|
148
|
+
end
|
|
149
|
+
rescue StandardError
|
|
150
|
+
# 客户端断开
|
|
151
|
+
ensure
|
|
152
|
+
@sse_mutex.synchronize { @sse_clients.delete(q) }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def _build_info
|
|
156
|
+
cfg = Config.get_config
|
|
157
|
+
te = cfg.telemetry_enabled
|
|
158
|
+
te = true if te.nil?
|
|
159
|
+
{
|
|
160
|
+
language: "Ruby",
|
|
161
|
+
version: VERSION,
|
|
162
|
+
host: @host,
|
|
163
|
+
port: @port,
|
|
164
|
+
proxy: cfg.proxy || "",
|
|
165
|
+
timeout: cfg.timeout,
|
|
166
|
+
config: { telemetry: te == true, insecure: cfg.insecure == true }
|
|
167
|
+
}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def _build_channels
|
|
171
|
+
Registry::REGISTRY.map { |s| { channel: s.channel, name: s.name, website: s.website } }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def _rendered_html
|
|
175
|
+
@_rendered_html ||= HTML_TEMPLATE.sub("<!-- PLACEHOLDER_SCRIPT -->", APP_SCRIPT)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# 自动检查环境变量并启动
|
|
180
|
+
def auto_start
|
|
181
|
+
flag = (ENV["TEMPMAIL_WEBUI"] || "").strip.downcase
|
|
182
|
+
WebUI.start if %w[1 true yes on].include?(flag)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# rubocop:disable Layout/LineLength
|
|
186
|
+
HTML_TEMPLATE = <<~'HTML'
|
|
187
|
+
<!DOCTYPE html>
|
|
188
|
+
<html lang="zh-CN">
|
|
189
|
+
<head>
|
|
190
|
+
<meta charset="UTF-8">
|
|
191
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
192
|
+
<title>TempMail SDK - WebUI</title>
|
|
193
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
194
|
+
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
|
195
|
+
<style>
|
|
196
|
+
[v-cloak]{display:none}
|
|
197
|
+
.log-debug{color:#6b7280}.log-info{color:#3b82f6}.log-warn{color:#f59e0b}.log-error{color:#ef4444}
|
|
198
|
+
.fade-enter-active{transition:opacity .3s}.fade-enter-from{opacity:0}
|
|
199
|
+
#log-container{max-height:60vh;overflow-y:auto;scroll-behavior:smooth}
|
|
200
|
+
</style>
|
|
201
|
+
</head>
|
|
202
|
+
<body class="bg-gray-900 text-gray-100 min-h-screen">
|
|
203
|
+
<div id="app" v-cloak>
|
|
204
|
+
<header class="bg-gray-800 border-b border-gray-700 px-6 py-4 flex items-center justify-between">
|
|
205
|
+
<div class="flex items-center gap-3">
|
|
206
|
+
<h1 class="text-xl font-bold">TempMail SDK</h1>
|
|
207
|
+
<span class="text-sm text-gray-400">{{ sdkInfo.language }} · v{{ sdkInfo.version }}</span>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="flex items-center gap-4 text-sm">
|
|
210
|
+
<span class="px-2 py-1 rounded" :class="connected ? 'bg-green-900 text-green-300' : 'bg-red-900 text-red-300'">
|
|
211
|
+
{{ connected ? '已连接' : '已断开' }}
|
|
212
|
+
</span>
|
|
213
|
+
<span class="text-gray-400">端口 {{ sdkInfo.port }}</span>
|
|
214
|
+
</div>
|
|
215
|
+
</header>
|
|
216
|
+
<nav class="bg-gray-800 border-b border-gray-700 px-6">
|
|
217
|
+
<div class="flex gap-1">
|
|
218
|
+
<button v-for="tab in tabs" :key="tab.id" @click="activeTab=tab.id"
|
|
219
|
+
class="px-4 py-2 text-sm rounded-t transition-colors"
|
|
220
|
+
:class="activeTab===tab.id ? 'bg-gray-900 text-white' : 'text-gray-400 hover:text-gray-200'">
|
|
221
|
+
{{ tab.label }}
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
</nav>
|
|
225
|
+
<main class="p-6">
|
|
226
|
+
<section v-show="activeTab==='overview'">
|
|
227
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
228
|
+
<div class="bg-gray-800 rounded-lg p-4">
|
|
229
|
+
<div class="text-gray-400 text-sm">渠道总数</div>
|
|
230
|
+
<div class="text-2xl font-bold">{{ channels.length }}</div>
|
|
231
|
+
</div>
|
|
232
|
+
<div class="bg-gray-800 rounded-lg p-4">
|
|
233
|
+
<div class="text-gray-400 text-sm">代理</div>
|
|
234
|
+
<div class="text-lg font-mono">{{ sdkInfo.proxy || '未设置' }}</div>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="bg-gray-800 rounded-lg p-4">
|
|
237
|
+
<div class="text-gray-400 text-sm">超时</div>
|
|
238
|
+
<div class="text-lg">{{ sdkInfo.timeout }}s</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="bg-gray-800 rounded-lg p-4">
|
|
242
|
+
<h3 class="text-sm text-gray-400 mb-2">配置</h3>
|
|
243
|
+
<pre class="text-xs font-mono text-gray-300 whitespace-pre-wrap">{{ JSON.stringify(sdkInfo.config, null, 2) }}</pre>
|
|
244
|
+
</div>
|
|
245
|
+
</section>
|
|
246
|
+
<section v-show="activeTab==='channels'">
|
|
247
|
+
<div class="mb-4">
|
|
248
|
+
<input v-model="channelSearch" type="text" placeholder="搜索渠道..."
|
|
249
|
+
class="bg-gray-800 border border-gray-600 rounded px-3 py-2 text-sm w-64 focus:outline-none focus:border-blue-500">
|
|
250
|
+
</div>
|
|
251
|
+
<div class="bg-gray-800 rounded-lg overflow-hidden">
|
|
252
|
+
<table class="w-full text-sm">
|
|
253
|
+
<thead class="bg-gray-750">
|
|
254
|
+
<tr class="border-b border-gray-700">
|
|
255
|
+
<th class="px-4 py-2 text-left text-gray-400">#</th>
|
|
256
|
+
<th class="px-4 py-2 text-left text-gray-400">渠道标识</th>
|
|
257
|
+
<th class="px-4 py-2 text-left text-gray-400">名称</th>
|
|
258
|
+
<th class="px-4 py-2 text-left text-gray-400">网站</th>
|
|
259
|
+
</tr>
|
|
260
|
+
</thead>
|
|
261
|
+
<tbody>
|
|
262
|
+
<tr v-for="(ch, i) in filteredChannels" :key="ch.channel"
|
|
263
|
+
class="border-b border-gray-700/50 hover:bg-gray-700/30">
|
|
264
|
+
<td class="px-4 py-2 text-gray-500">{{ i + 1 }}</td>
|
|
265
|
+
<td class="px-4 py-2 font-mono">{{ ch.channel }}</td>
|
|
266
|
+
<td class="px-4 py-2">{{ ch.name }}</td>
|
|
267
|
+
<td class="px-4 py-2"><a :href="'https://'+ch.website" target="_blank" class="text-blue-400 hover:underline">{{ ch.website }}</a></td>
|
|
268
|
+
</tr>
|
|
269
|
+
</tbody>
|
|
270
|
+
</table>
|
|
271
|
+
</div>
|
|
272
|
+
</section>
|
|
273
|
+
<section v-show="activeTab==='logs'">
|
|
274
|
+
<div class="flex items-center gap-3 mb-4">
|
|
275
|
+
<select v-model="logLevel" class="bg-gray-800 border border-gray-600 rounded px-2 py-1 text-sm">
|
|
276
|
+
<option value="all">全部</option>
|
|
277
|
+
<option value="debug">Debug</option>
|
|
278
|
+
<option value="info">Info</option>
|
|
279
|
+
<option value="warn">Warn</option>
|
|
280
|
+
<option value="error">Error</option>
|
|
281
|
+
</select>
|
|
282
|
+
<button @click="logs=[]" class="text-sm text-gray-400 hover:text-white">清空</button>
|
|
283
|
+
<label class="text-sm text-gray-400 flex items-center gap-1">
|
|
284
|
+
<input type="checkbox" v-model="autoScroll"> 自动滚动
|
|
285
|
+
</label>
|
|
286
|
+
<span class="text-xs text-gray-500 ml-auto">{{ logs.length }} 条</span>
|
|
287
|
+
</div>
|
|
288
|
+
<div id="log-container" ref="logContainer" class="bg-gray-950 rounded-lg p-4 font-mono text-xs">
|
|
289
|
+
<div v-for="(log, i) in filteredLogs" :key="i" class="py-0.5" :class="'log-'+log.level">
|
|
290
|
+
<span class="text-gray-600">{{ log.time }}</span>
|
|
291
|
+
<span class="px-1">[{{ log.level.toUpperCase() }}]</span>
|
|
292
|
+
<span v-if="log.channel" class="text-purple-400">[{{ log.channel }}]</span>
|
|
293
|
+
{{ log.msg }}
|
|
294
|
+
</div>
|
|
295
|
+
<div v-if="filteredLogs.length===0" class="text-gray-600">暂无日志</div>
|
|
296
|
+
</div>
|
|
297
|
+
</section>
|
|
298
|
+
</main>
|
|
299
|
+
</div>
|
|
300
|
+
<!-- PLACEHOLDER_SCRIPT -->
|
|
301
|
+
</body>
|
|
302
|
+
</html>
|
|
303
|
+
HTML
|
|
304
|
+
|
|
305
|
+
APP_SCRIPT = <<~'JS'
|
|
306
|
+
<script>
|
|
307
|
+
const {createApp, ref, computed, onMounted, onUnmounted, nextTick} = Vue
|
|
308
|
+
createApp({
|
|
309
|
+
setup() {
|
|
310
|
+
const tabs = [{id:'overview',label:'概览'},{id:'channels',label:'渠道'},{id:'logs',label:'日志'}]
|
|
311
|
+
const activeTab = ref('overview')
|
|
312
|
+
const connected = ref(false)
|
|
313
|
+
const sdkInfo = ref({language:'',version:'',port:'',proxy:'',timeout:30,config:{}})
|
|
314
|
+
const channels = ref([])
|
|
315
|
+
const logs = ref([])
|
|
316
|
+
const channelSearch = ref('')
|
|
317
|
+
const logLevel = ref('all')
|
|
318
|
+
const autoScroll = ref(true)
|
|
319
|
+
const logContainer = ref(null)
|
|
320
|
+
let evtSource = null
|
|
321
|
+
const filteredChannels = computed(() => {
|
|
322
|
+
if (!channelSearch.value) return channels.value
|
|
323
|
+
const q = channelSearch.value.toLowerCase()
|
|
324
|
+
return channels.value.filter(c =>
|
|
325
|
+
c.channel.includes(q) || c.name.toLowerCase().includes(q) || c.website.includes(q))
|
|
326
|
+
})
|
|
327
|
+
const filteredLogs = computed(() => {
|
|
328
|
+
if (logLevel.value === 'all') return logs.value
|
|
329
|
+
return logs.value.filter(l => l.level === logLevel.value)
|
|
330
|
+
})
|
|
331
|
+
async function fetchInfo() {
|
|
332
|
+
try { const r = await fetch('/api/info'); if (r.ok) sdkInfo.value = await r.json() } catch(e) {}
|
|
333
|
+
}
|
|
334
|
+
async function fetchChannels() {
|
|
335
|
+
try { const r = await fetch('/api/channels'); if (r.ok) channels.value = await r.json() } catch(e) {}
|
|
336
|
+
}
|
|
337
|
+
function connectSSE() {
|
|
338
|
+
evtSource = new EventSource('/api/logs/stream')
|
|
339
|
+
evtSource.onopen = () => { connected.value = true }
|
|
340
|
+
evtSource.onmessage = (e) => {
|
|
341
|
+
try {
|
|
342
|
+
const entry = JSON.parse(e.data)
|
|
343
|
+
logs.value.push(entry)
|
|
344
|
+
if (logs.value.length > 5000) logs.value.splice(0, 500)
|
|
345
|
+
if (autoScroll.value) nextTick(() => { const el = logContainer.value; if (el) el.scrollTop = el.scrollHeight })
|
|
346
|
+
} catch(err) {}
|
|
347
|
+
}
|
|
348
|
+
evtSource.onerror = () => { connected.value = false; setTimeout(connectSSE, 3000) }
|
|
349
|
+
}
|
|
350
|
+
onMounted(() => { fetchInfo(); fetchChannels(); connectSSE() })
|
|
351
|
+
onUnmounted(() => { if (evtSource) evtSource.close() })
|
|
352
|
+
return {tabs,activeTab,connected,sdkInfo,channels,logs,channelSearch,logLevel,
|
|
353
|
+
autoScroll,logContainer,filteredChannels,filteredLogs}
|
|
354
|
+
}
|
|
355
|
+
}).mount('#app')
|
|
356
|
+
</script>
|
|
357
|
+
JS
|
|
358
|
+
# rubocop:enable Layout/LineLength
|
|
359
|
+
end
|
|
360
|
+
end
|
data/lib/tempmail_sdk.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# 临时邮箱 SDK (Ruby)
|
|
4
|
+
# 聚合 279 个第三方临时邮箱渠道,所有渠道返回统一标准化格式。
|
|
5
|
+
# 与 Go / npm / Rust / Python / C / PHP 各端共享相同的渠道标识与顺序。
|
|
6
|
+
|
|
7
|
+
require_relative "tempmail_sdk/version"
|
|
8
|
+
require_relative "tempmail_sdk/types"
|
|
9
|
+
require_relative "tempmail_sdk/config"
|
|
10
|
+
require_relative "tempmail_sdk/logger"
|
|
11
|
+
require_relative "tempmail_sdk/html_utils"
|
|
12
|
+
require_relative "tempmail_sdk/normalize"
|
|
13
|
+
require_relative "tempmail_sdk/http"
|
|
14
|
+
require_relative "tempmail_sdk/retry"
|
|
15
|
+
require_relative "tempmail_sdk/telemetry"
|
|
16
|
+
require_relative "tempmail_sdk/channels"
|
|
17
|
+
require_relative "tempmail_sdk/registry"
|
|
18
|
+
require_relative "tempmail_sdk/client"
|
|
19
|
+
require_relative "tempmail_sdk/webui"
|
|
20
|
+
|
|
21
|
+
# 顶层命名空间:聚合各端一致的临时邮箱 SDK 能力
|
|
22
|
+
module TempmailSdk
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
# 创建临时邮箱(无状态入口,支持渠道 fallback)
|
|
26
|
+
# @param options [GenerateEmailOptions, nil]
|
|
27
|
+
# @return [EmailInfo, nil]
|
|
28
|
+
def generate_email(options = nil)
|
|
29
|
+
Client.generate_email(options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# 获取邮件列表(无状态入口)
|
|
33
|
+
# @param info [EmailInfo]
|
|
34
|
+
# @param options [GetEmailsOptions, nil]
|
|
35
|
+
# @return [GetEmailsResult]
|
|
36
|
+
def get_emails(info, options = nil)
|
|
37
|
+
Client.get_emails(info, options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# 获取所有支持的渠道列表(有序)
|
|
41
|
+
# @return [Array<ChannelInfo>]
|
|
42
|
+
def list_channels
|
|
43
|
+
Client.list_channels
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# 获取指定渠道的详细信息
|
|
47
|
+
# @param channel [String]
|
|
48
|
+
# @return [ChannelInfo, nil]
|
|
49
|
+
def channel_info(channel)
|
|
50
|
+
Client.channel_info(channel)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# 设置 SDK 全局配置
|
|
54
|
+
def set_config(config = nil, **kwargs)
|
|
55
|
+
Config.set_config(config, **kwargs)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# 获取当前全局配置
|
|
59
|
+
# @return [SDKConfig]
|
|
60
|
+
def config
|
|
61
|
+
Config.get_config
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# 设置日志级别
|
|
65
|
+
def set_log_level(level)
|
|
66
|
+
Log.set_log_level(level)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [String] SDK 版本号
|
|
70
|
+
def sdk_version
|
|
71
|
+
VERSION
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# 加载后按环境变量自动启动 WebUI(默认不启动)
|
|
76
|
+
TempmailSdk::WebUI.auto_start
|
metadata
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: tempmail_sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.3.3
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- tempmail-sdk
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-07-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: 五端(Go/npm/Rust/Python/C)之外的 Ruby 版临时邮箱 SDK,共享相同的渠道标识与顺序,提供统一的 Email 结构、渠道
|
|
14
|
+
fallback、代理/超时/TLS/遥测配置。
|
|
15
|
+
email:
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- README.md
|
|
21
|
+
- lib/tempmail_sdk.rb
|
|
22
|
+
- lib/tempmail_sdk/channels.rb
|
|
23
|
+
- lib/tempmail_sdk/channels_data.rb
|
|
24
|
+
- lib/tempmail_sdk/client.rb
|
|
25
|
+
- lib/tempmail_sdk/config.rb
|
|
26
|
+
- lib/tempmail_sdk/html_utils.rb
|
|
27
|
+
- lib/tempmail_sdk/http.rb
|
|
28
|
+
- lib/tempmail_sdk/logger.rb
|
|
29
|
+
- lib/tempmail_sdk/normalize.rb
|
|
30
|
+
- lib/tempmail_sdk/providers/anonbox.rb
|
|
31
|
+
- lib/tempmail_sdk/providers/anonymmail.rb
|
|
32
|
+
- lib/tempmail_sdk/providers/byom.rb
|
|
33
|
+
- lib/tempmail_sdk/providers/catchmail.rb
|
|
34
|
+
- lib/tempmail_sdk/providers/cleantempmail.rb
|
|
35
|
+
- lib/tempmail_sdk/providers/devmail_uk.rb
|
|
36
|
+
- lib/tempmail_sdk/providers/dropmail.rb
|
|
37
|
+
- lib/tempmail_sdk/providers/dropmail_click.rb
|
|
38
|
+
- lib/tempmail_sdk/providers/dropmail_me.rb
|
|
39
|
+
- lib/tempmail_sdk/providers/duckmail.rb
|
|
40
|
+
- lib/tempmail_sdk/providers/eyepaste.rb
|
|
41
|
+
- lib/tempmail_sdk/providers/fake_email_site.rb
|
|
42
|
+
- lib/tempmail_sdk/providers/fake_legal.rb
|
|
43
|
+
- lib/tempmail_sdk/providers/fmail.rb
|
|
44
|
+
- lib/tempmail_sdk/providers/freecustom.rb
|
|
45
|
+
- lib/tempmail_sdk/providers/getnada.rb
|
|
46
|
+
- lib/tempmail_sdk/providers/gonebox_email.rb
|
|
47
|
+
- lib/tempmail_sdk/providers/guerrillamail.rb
|
|
48
|
+
- lib/tempmail_sdk/providers/guerrillamail_mirrors.rb
|
|
49
|
+
- lib/tempmail_sdk/providers/harakirimail.rb
|
|
50
|
+
- lib/tempmail_sdk/providers/haribu.rb
|
|
51
|
+
- lib/tempmail_sdk/providers/inboxes.rb
|
|
52
|
+
- lib/tempmail_sdk/providers/inboxkitten.rb
|
|
53
|
+
- lib/tempmail_sdk/providers/linshiyou.rb
|
|
54
|
+
- lib/tempmail_sdk/providers/lroid.rb
|
|
55
|
+
- lib/tempmail_sdk/providers/m2u.rb
|
|
56
|
+
- lib/tempmail_sdk/providers/mail10s.rb
|
|
57
|
+
- lib/tempmail_sdk/providers/mail123.rb
|
|
58
|
+
- lib/tempmail_sdk/providers/mail_cx.rb
|
|
59
|
+
- lib/tempmail_sdk/providers/mail_sunls.rb
|
|
60
|
+
- lib/tempmail_sdk/providers/mail_tm.rb
|
|
61
|
+
- lib/tempmail_sdk/providers/mailcatch.rb
|
|
62
|
+
- lib/tempmail_sdk/providers/maildrop.rb
|
|
63
|
+
- lib/tempmail_sdk/providers/maildrop_cc.rb
|
|
64
|
+
- lib/tempmail_sdk/providers/mailforspam.rb
|
|
65
|
+
- lib/tempmail_sdk/providers/mailhole_de.rb
|
|
66
|
+
- lib/tempmail_sdk/providers/mailinator.rb
|
|
67
|
+
- lib/tempmail_sdk/providers/mailmomy.rb
|
|
68
|
+
- lib/tempmail_sdk/providers/mailnesia.rb
|
|
69
|
+
- lib/tempmail_sdk/providers/mffac.rb
|
|
70
|
+
- lib/tempmail_sdk/providers/moakt.rb
|
|
71
|
+
- lib/tempmail_sdk/providers/neighbours.rb
|
|
72
|
+
- lib/tempmail_sdk/providers/neighbours_sh.rb
|
|
73
|
+
- lib/tempmail_sdk/providers/nimail.rb
|
|
74
|
+
- lib/tempmail_sdk/providers/ockito.rb
|
|
75
|
+
- lib/tempmail_sdk/providers/one_sec_mail.rb
|
|
76
|
+
- lib/tempmail_sdk/providers/restmail_net.rb
|
|
77
|
+
- lib/tempmail_sdk/providers/rootsh.rb
|
|
78
|
+
- lib/tempmail_sdk/providers/shitty_email.rb
|
|
79
|
+
- lib/tempmail_sdk/providers/smailpro.rb
|
|
80
|
+
- lib/tempmail_sdk/providers/ta_easy.rb
|
|
81
|
+
- lib/tempmail_sdk/providers/temp_mail_io.rb
|
|
82
|
+
- lib/tempmail_sdk/providers/temp_mail_now.rb
|
|
83
|
+
- lib/tempmail_sdk/providers/temp_mail_org.rb
|
|
84
|
+
- lib/tempmail_sdk/providers/tempemail_co.rb
|
|
85
|
+
- lib/tempmail_sdk/providers/tempemail_info.rb
|
|
86
|
+
- lib/tempmail_sdk/providers/tempemails_net.rb
|
|
87
|
+
- lib/tempmail_sdk/providers/tempfastmail.rb
|
|
88
|
+
- lib/tempmail_sdk/providers/tempgmailer.rb
|
|
89
|
+
- lib/tempmail_sdk/providers/tempgo_email.rb
|
|
90
|
+
- lib/tempmail_sdk/providers/tempinbox.rb
|
|
91
|
+
- lib/tempmail_sdk/providers/tempmail.rb
|
|
92
|
+
- lib/tempmail_sdk/providers/tempmail365.rb
|
|
93
|
+
- lib/tempmail_sdk/providers/tempmail_fish.rb
|
|
94
|
+
- lib/tempmail_sdk/providers/tempmail_fyi.rb
|
|
95
|
+
- lib/tempmail_sdk/providers/tempmail_lol.rb
|
|
96
|
+
- lib/tempmail_sdk/providers/tempmail_lol_v2.rb
|
|
97
|
+
- lib/tempmail_sdk/providers/tempmail_plus.rb
|
|
98
|
+
- lib/tempmail_sdk/providers/tempmailc.rb
|
|
99
|
+
- lib/tempmail_sdk/providers/tempmailpro.rb
|
|
100
|
+
- lib/tempmail_sdk/providers/tempmailten.rb
|
|
101
|
+
- lib/tempmail_sdk/providers/temporam.rb
|
|
102
|
+
- lib/tempmail_sdk/providers/tempp_mails.rb
|
|
103
|
+
- lib/tempmail_sdk/providers/tempy_email.rb
|
|
104
|
+
- lib/tempmail_sdk/providers/ten_minute_mail_net.rb
|
|
105
|
+
- lib/tempmail_sdk/providers/tenminute_one.rb
|
|
106
|
+
- lib/tempmail_sdk/providers/throwawaymail.rb
|
|
107
|
+
- lib/tempmail_sdk/providers/tmail_link.rb
|
|
108
|
+
- lib/tempmail_sdk/providers/uncorreotemporal.rb
|
|
109
|
+
- lib/tempmail_sdk/providers/webmailtemp.rb
|
|
110
|
+
- lib/tempmail_sdk/providers/xkx_me.rb
|
|
111
|
+
- lib/tempmail_sdk/providers/zhujump.rb
|
|
112
|
+
- lib/tempmail_sdk/registry.rb
|
|
113
|
+
- lib/tempmail_sdk/retry.rb
|
|
114
|
+
- lib/tempmail_sdk/telemetry.rb
|
|
115
|
+
- lib/tempmail_sdk/types.rb
|
|
116
|
+
- lib/tempmail_sdk/version.rb
|
|
117
|
+
- lib/tempmail_sdk/webui.rb
|
|
118
|
+
homepage: https://github.com/tempmail-sdk/tempmail-sdk
|
|
119
|
+
licenses:
|
|
120
|
+
- GPL-3.0-only
|
|
121
|
+
metadata:
|
|
122
|
+
rubygems_mfa_required: 'false'
|
|
123
|
+
post_install_message:
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
require_paths:
|
|
126
|
+
- lib
|
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '3.0'
|
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - ">="
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '0'
|
|
137
|
+
requirements: []
|
|
138
|
+
rubygems_version: 3.4.19
|
|
139
|
+
signing_key:
|
|
140
|
+
specification_version: 4
|
|
141
|
+
summary: 临时邮箱 SDK,聚合 279 个第三方临时邮箱渠道,返回统一标准化格式
|
|
142
|
+
test_files: []
|