File size: 14,589 Bytes
662a165
 
 
 
 
e72fc95
662a165
 
 
 
e72fc95
 
 
 
662a165
 
e72fc95
 
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
 
662a165
 
 
 
 
 
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662a165
 
e72fc95
 
 
662a165
e72fc95
 
662a165
e72fc95
 
 
 
 
662a165
e72fc95
 
 
 
 
662a165
e72fc95
 
 
 
 
662a165
 
e72fc95
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662a165
 
e72fc95
 
 
 
662a165
e72fc95
662a165
e72fc95
 
 
 
662a165
e72fc95
 
 
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
 
 
 
 
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
 
 
 
 
 
662a165
e72fc95
 
 
 
 
 
 
 
 
 
662a165
e72fc95
662a165
e72fc95
 
 
 
 
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
 
 
 
 
 
 
 
662a165
e72fc95
 
 
 
 
 
 
 
 
 
662a165
e72fc95
 
 
 
662a165
e72fc95
 
 
662a165
e72fc95
 
 
 
 
 
662a165
e72fc95
 
 
 
 
 
 
662a165
e72fc95
 
662a165
 
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
662a165
e72fc95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662a165
e72fc95
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Professional Temp Mail - Full Version</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
        body { font-family: 'Inter', sans-serif; background-color: #0f172a; color: #f1f5f9; }
        .dark-card { background: #1e293b; border: 1px solid #334155; }
        .mail-content-wrapper { background: white; border-radius: 8px; overflow: hidden; min-height: 400px; }
        .mail-content-wrapper iframe { width: 100%; border: none; height: 100%; min-height: 400px; }
        .loader { border-top-color: #6366f1; animation: spinner 0.6s linear infinite; }
        @keyframes spinner { to { transform: rotate(360deg); } }
        .animate-fade-in { animation: fadeIn 0.3s ease-out forwards; }
        @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
        
        /* Attachment styling */
        .attachment-chip {
            background: #f1f5f9;
            color: #334155;
            padding: 6px 12px;
            border-radius: 6px;
            font-size: 12px;
            display: flex;
            align-items: center;
            gap: 8px;
            border: 1px solid #e2e8f0;
            transition: all 0.2s;
        }
        .attachment-chip:hover {
            background: #e2e8f0;
            color: #4f46e5;
        }
    </style>
</head>
<body class="min-h-screen py-10 px-4">

<div class="max-w-4xl mx-auto">
    <!-- Header -->
    <div class="mb-8">
        <h1 class="text-3xl font-bold text-white flex items-center gap-3">
            <i class="fa-solid fa-shield-halved text-indigo-500"></i> Temp Mail Pro
        </h1>
        <p class="text-slate-400 mt-1">Dịch vụ email tạm thời - Hỗ trợ tệp đính kèm</p>
    </div>

    <!-- Mail Creation Area -->
    <div class="dark-card rounded-2xl p-6 shadow-2xl mb-6">
        <label class="block text-sm font-medium text-slate-400 mb-2 uppercase tracking-wider">Địa chỉ email hiện tại</label>
        <div class="flex flex-col sm:flex-row gap-3">
            <div class="relative flex-grow">
                <input type="text" id="emailAddr" readonly placeholder="Đang khởi tạo..." class="w-full bg-slate-900 border border-slate-700 rounded-xl py-3 px-4 pr-12 font-mono text-indigo-400 focus:outline-none focus:border-indigo-500">
                <button onclick="copyEmail()" class="absolute right-3 top-3 text-slate-500 hover:text-indigo-400 transition-colors">
                    <i class="fa-regular fa-copy text-xl"></i>
                </button>
            </div>
            <button onclick="createNewAccount()" class="bg-slate-700 hover:bg-slate-600 text-white font-semibold py-3 px-6 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95">
                <i class="fa-solid fa-rotate"></i> Đổi địa chỉ
            </button>
        </div>
        <div id="copyToast" class="hidden text-xs text-emerald-400 mt-2 font-medium text-center">Đã sao chép địa chỉ vào clipboard</div>
    </div>

    <!-- Controls -->
    <div class="flex items-center justify-between mb-4 px-2">
        <div class="flex items-center gap-3">
            <h2 class="text-xl font-bold text-slate-200">Hộp thư đến</h2>
            <div id="refreshLoader" class="hidden w-5 h-5 border-2 border-slate-600 loader rounded-full"></div>
        </div>
        
        <button onclick="checkInbox()" class="bg-indigo-600/10 hover:bg-indigo-600/20 text-indigo-400 text-sm font-semibold py-2 px-4 rounded-lg flex items-center gap-2 transition-all border border-indigo-500/20 active:scale-95">
            <i class="fa-solid fa-arrows-rotate" id="refreshIcon"></i> Làm mới hộp thư
        </button>
    </div>

    <!-- Inbox List -->
    <div id="inboxContainer" class="space-y-3">
        <div id="emptyState" class="text-center py-20 dark-card rounded-2xl border-dashed border-2 border-slate-700">
            <i class="fa-solid fa-inbox text-5xl text-slate-700 mb-4"></i>
            <p class="text-slate-500">Chưa có thư. Nhấn "Làm mới" để kiểm tra.</p>
        </div>
    </div>
</div>

<!-- Modal Xem Thư -->
<div id="mailModal" class="fixed inset-0 bg-slate-950/80 hidden z-50 flex items-center justify-center p-4">
    <div class="dark-card w-full max-w-4xl rounded-2xl shadow-2xl flex flex-col max-h-[90vh]">
        <div class="p-4 border-b border-slate-700 flex justify-between items-center bg-slate-800 rounded-t-2xl">
            <h3 class="font-bold text-slate-200 truncate pr-4" id="modalSubject">Tiêu đề thư</h3>
            <button onclick="closeMail()" class="text-slate-500 hover:text-white text-2xl px-2">&times;</button>
        </div>
        
        <!-- Mail Header Info -->
        <div class="p-4 bg-slate-900 border-b border-slate-700 text-sm">
            <div id="modalFrom" class="text-indigo-400 font-medium"></div>
            <div id="modalTime" class="text-slate-500 text-xs mt-1"></div>
            
            <!-- Attachments Area -->
            <div id="attachmentArea" class="hidden mt-4 pt-3 border-t border-slate-800">
                <p class="text-[10px] text-slate-500 uppercase tracking-widest mb-2">Tệp đính kèm:</p>
                <div id="attachmentList" class="flex flex-wrap gap-2"></div>
            </div>
        </div>

        <div class="p-4 overflow-auto flex-grow bg-slate-100 rounded-b-2xl">
            <div id="modalBody" class="mail-content-wrapper"></div>
        </div>
    </div>
</div>

<script>
    const API = "https://api.mail.tm";
    let currentToken = "";
    let currentEmail = "";

    async function init() {
        await createNewAccount();
    }

    async function createNewAccount() {
        try {
            const domains = await fetch(`${API}/domains`).then(r => r.json());
            const domain = domains['hydra:member'][0].domain;
            const user = Math.random().toString(36).substring(2, 12);
            const address = `${user}@${domain}`;
            const password = "password123";
            
            await fetch(`${API}/accounts`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({address, password})
            });

            const tokenData = await fetch(`${API}/token`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({address, password})
            }).then(r => r.json());

            currentToken = tokenData.token;
            currentEmail = address;
            document.getElementById('emailAddr').value = address;
            
            document.getElementById('inboxContainer').innerHTML = `
                <div id="emptyState" class="text-center py-20 dark-card rounded-2xl border-dashed border-2 border-slate-700">
                    <i class="fa-solid fa-inbox text-5xl text-slate-700 mb-4"></i>
                    <p class="text-slate-500">Đang chờ thư mới...</p>
                </div>`;
            checkInbox();
        } catch (err) { console.error(err); }
    }

    async function checkInbox() {
        if (!currentToken) return;
        const loader = document.getElementById('refreshLoader');
        const icon = document.getElementById('refreshIcon');
        loader.classList.remove('hidden');
        if (icon) icon.classList.add('fa-spin');

        try {
            const res = await fetch(`${API}/messages`, {
                headers: {'Authorization': `Bearer ${currentToken}`}
            }).then(r => r.json());
            renderInbox(res['hydra:member']);
        } catch (err) { console.error(err); } finally {
            setTimeout(() => {
                loader.classList.add('hidden');
                if (icon) icon.classList.remove('fa-spin');
            }, 500);
        }
    }

    function renderInbox(msgs) {
        const container = document.getElementById('inboxContainer');
        const empty = document.getElementById('emptyState');
        if (!msgs || msgs.length === 0) return;
        if (empty) empty.remove();

        const currentIds = Array.from(container.querySelectorAll('.mail-item')).map(m => m.dataset.id);
        
        msgs.forEach(m => {
            if (!currentIds.includes(m.id)) {
                const div = document.createElement('div');
                div.className = "mail-item bg-slate-800 p-4 rounded-xl border border-slate-700 hover:border-indigo-500 transition-all cursor-pointer flex justify-between items-center group shadow-sm animate-fade-in";
                div.dataset.id = m.id;
                div.onclick = () => openMail(m.id);
                
                // Hiển thị icon nếu có file đính kèm
                const hasAttach = m.hasAttachments ? '<i class="fa-solid fa-paperclip text-slate-500 mr-2"></i>' : '';

                div.innerHTML = `
                    <div class="flex-grow mr-4 overflow-hidden">
                        <div class="flex items-center gap-2 mb-1">
                            <span class="text-xs font-bold text-indigo-400 uppercase tracking-tighter truncate">${m.from.address}</span>
                            <span class="text-[10px] text-slate-500 whitespace-nowrap">${new Date(m.createdAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
                        </div>
                        <div class="font-semibold text-slate-200 truncate">${hasAttach}${m.subject || '(Không có tiêu đề)'}</div>
                    </div>
                    <i class="fa-solid fa-arrow-right-long text-slate-600 group-hover:text-indigo-400 group-hover:translate-x-1 transition-all"></i>
                `;
                container.prepend(div);
            }
        });
    }

    async function openMail(id) {
        const modal = document.getElementById('mailModal');
        const bodyWrapper = document.getElementById('modalBody');
        const attachmentArea = document.getElementById('attachmentArea');
        const attachmentList = document.getElementById('attachmentList');
        
        bodyWrapper.innerHTML = '<div class="flex justify-center p-20"><div class="loader w-10 h-10 border-4 rounded-full"></div></div>';
        attachmentArea.classList.add('hidden');
        attachmentList.innerHTML = '';
        modal.classList.remove('hidden');

        try {
            const m = await fetch(`${API}/messages/${id}`, {
                headers: {'Authorization': `Bearer ${currentToken}`}
            }).then(r => r.json());

            document.getElementById('modalSubject').innerText = m.subject || 'No Subject';
            document.getElementById('modalFrom').innerText = `Từ: ${m.from.address}`;
            document.getElementById('modalTime').innerText = new Date(m.createdAt).toLocaleString();

            // Xử lý tệp đính kèm
            if (m.attachments && m.attachments.length > 0) {
                attachmentArea.classList.remove('hidden');
                m.attachments.forEach(file => {
                    const downloadUrl = `${API}${file.downloadUrl}`;
                    const fileSize = (file.size / 1024).toFixed(1) + ' KB';
                    
                    const chip = document.createElement('a');
                    chip.href = "javascript:void(0)";
                    chip.onclick = (e) => downloadFile(downloadUrl, file.filename);
                    chip.className = "attachment-chip";
                    chip.innerHTML = `
                        <i class="fa-solid fa-file-arrow-down"></i>
                        <span>${file.filename} <span class="text-slate-400">(${fileSize})</span></span>
                    `;
                    attachmentList.appendChild(chip);
                });
            }

            if (m.html && m.html.length > 0) {
                bodyWrapper.innerHTML = `<iframe id="mailIframe" title="Email Content"></iframe>`;
                const iframe = document.getElementById('mailIframe');
                const doc = iframe.contentWindow.document;
                doc.open();
                doc.write(`<style>body{font-family:'Inter',sans-serif;line-height:1.6;color:#333;}img{max-width:100%;height:auto;}a{color:#4f46e5;}</style>${m.html[0]}`);
                doc.close();
                iframe.onload = () => { iframe.style.height = doc.body.scrollHeight + 'px'; };
            } else {
                const textWithLinks = m.text.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1" target="_blank" rel="noopener noreferrer" style="color:#4f46e5;text-decoration:underline;">$1</a>');
                bodyWrapper.innerHTML = `<div class="p-6 bg-white h-full overflow-auto" style="color:#1e293b; line-height:1.6; white-space:pre-wrap; word-break:break-word;">${textWithLinks}</div>`;
            }
        } catch (err) {
            bodyWrapper.innerHTML = '<p class="text-red-500 text-center p-10">Lỗi tải nội dung thư. Vui lòng thử lại.</p>';
        }
    }

    // Hàm tải tệp thực tế sử dụng Bearer Token
    async function downloadFile(url, filename) {
        try {
            const response = await fetch(url, {
                headers: { 'Authorization': `Bearer ${currentToken}` }
            });
            const blob = await response.blob();
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);
            link.download = filename;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        } catch (err) {
            console.error("Lỗi tải tệp:", err);
            alert("Không thể tải tệp. Vui lòng kiểm tra kết nối.");
        }
    }

    function closeMail() {
        document.getElementById('mailModal').classList.add('hidden');
    }

    function copyEmail() {
        const el = document.getElementById('emailAddr');
        el.select();
        document.execCommand('copy');
        const toast = document.getElementById('copyToast');
        toast.classList.remove('hidden');
        setTimeout(() => toast.classList.add('hidden'), 2000);
    }

    window.onload = init;
</script>
</body>
</html>