archicore 0.3.1 → 0.3.2
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.
- package/README.md +48 -4
- package/dist/cli/commands/interactive.js +83 -23
- package/dist/cli/commands/projects.js +3 -3
- package/dist/cli/ui/prompt.d.ts +4 -0
- package/dist/cli/ui/prompt.js +22 -0
- package/dist/cli/utils/config.js +2 -2
- package/dist/cli/utils/upload-utils.js +65 -18
- package/dist/code-index/ast-parser.d.ts +4 -0
- package/dist/code-index/ast-parser.js +42 -0
- package/dist/code-index/index.d.ts +21 -1
- package/dist/code-index/index.js +45 -1
- package/dist/code-index/source-map-extractor.d.ts +71 -0
- package/dist/code-index/source-map-extractor.js +194 -0
- package/dist/gitlab/gitlab-service.d.ts +162 -0
- package/dist/gitlab/gitlab-service.js +652 -0
- package/dist/gitlab/index.d.ts +8 -0
- package/dist/gitlab/index.js +8 -0
- package/dist/server/config/passport.d.ts +14 -0
- package/dist/server/config/passport.js +86 -0
- package/dist/server/index.js +52 -10
- package/dist/server/middleware/api-auth.d.ts +2 -2
- package/dist/server/middleware/api-auth.js +21 -2
- package/dist/server/middleware/csrf.d.ts +23 -0
- package/dist/server/middleware/csrf.js +96 -0
- package/dist/server/routes/auth.d.ts +2 -2
- package/dist/server/routes/auth.js +204 -5
- package/dist/server/routes/device-auth.js +2 -2
- package/dist/server/routes/gitlab.d.ts +12 -0
- package/dist/server/routes/gitlab.js +528 -0
- package/dist/server/routes/oauth.d.ts +6 -0
- package/dist/server/routes/oauth.js +198 -0
- package/dist/server/services/audit-service.d.ts +1 -1
- package/dist/server/services/auth-service.d.ts +13 -1
- package/dist/server/services/auth-service.js +108 -7
- package/dist/server/services/email-service.d.ts +63 -0
- package/dist/server/services/email-service.js +586 -0
- package/dist/server/utils/disposable-email-domains.d.ts +14 -0
- package/dist/server/utils/disposable-email-domains.js +192 -0
- package/dist/types/api.d.ts +98 -0
- package/dist/types/gitlab.d.ts +245 -0
- package/dist/types/gitlab.js +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
import { Logger } from '../../utils/logger.js';
|
|
3
|
+
class EmailService {
|
|
4
|
+
transporter = null;
|
|
5
|
+
config = null;
|
|
6
|
+
enabled = false;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.initializeFromEnv();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Initialize email service from environment variables
|
|
12
|
+
*/
|
|
13
|
+
initializeFromEnv() {
|
|
14
|
+
const { SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASS, EMAIL_FROM, EMAIL_FROM_NAME } = process.env;
|
|
15
|
+
// Check if all required environment variables are present
|
|
16
|
+
if (!SMTP_HOST || !SMTP_PORT || !SMTP_USER || !SMTP_PASS || !EMAIL_FROM) {
|
|
17
|
+
Logger.warn('[EmailService] SMTP configuration incomplete - email sending disabled');
|
|
18
|
+
Logger.warn('[EmailService] Required: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, EMAIL_FROM');
|
|
19
|
+
this.enabled = false;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
this.config = {
|
|
23
|
+
host: SMTP_HOST,
|
|
24
|
+
port: parseInt(SMTP_PORT, 10),
|
|
25
|
+
secure: SMTP_SECURE === 'true',
|
|
26
|
+
user: SMTP_USER,
|
|
27
|
+
pass: SMTP_PASS,
|
|
28
|
+
from: EMAIL_FROM,
|
|
29
|
+
fromName: EMAIL_FROM_NAME || 'ArchiCore'
|
|
30
|
+
};
|
|
31
|
+
this.createTransporter();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create nodemailer transporter
|
|
35
|
+
*/
|
|
36
|
+
createTransporter() {
|
|
37
|
+
if (!this.config) {
|
|
38
|
+
this.enabled = false;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
this.transporter = nodemailer.createTransport({
|
|
43
|
+
host: this.config.host,
|
|
44
|
+
port: this.config.port,
|
|
45
|
+
secure: this.config.secure,
|
|
46
|
+
auth: {
|
|
47
|
+
user: this.config.user,
|
|
48
|
+
pass: this.config.pass
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.enabled = true;
|
|
52
|
+
Logger.info(`[EmailService] Initialized with ${this.config.host}:${this.config.port}`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
Logger.error('[EmailService] Failed to create transporter:', error);
|
|
56
|
+
this.enabled = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify SMTP connection
|
|
61
|
+
*/
|
|
62
|
+
async verify() {
|
|
63
|
+
if (!this.transporter || !this.enabled) {
|
|
64
|
+
Logger.warn('[EmailService] Email service not enabled - skipping verification');
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
await this.transporter.verify();
|
|
69
|
+
Logger.info('[EmailService] SMTP connection verified successfully');
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
Logger.error('[EmailService] SMTP verification failed:', error);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Send email
|
|
79
|
+
*/
|
|
80
|
+
async send(options) {
|
|
81
|
+
if (!this.transporter || !this.enabled || !this.config) {
|
|
82
|
+
Logger.warn('[EmailService] Email service not enabled - email not sent');
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const mailOptions = {
|
|
87
|
+
from: `"${this.config.fromName}" <${this.config.from}>`,
|
|
88
|
+
to: options.to,
|
|
89
|
+
subject: options.subject,
|
|
90
|
+
html: options.html,
|
|
91
|
+
text: options.text || this.stripHtml(options.html)
|
|
92
|
+
};
|
|
93
|
+
const info = await this.transporter.sendMail(mailOptions);
|
|
94
|
+
Logger.info(`[EmailService] Email sent to ${options.to}: ${info.messageId}`);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
Logger.error(`[EmailService] Failed to send email to ${options.to}:`, error);
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Send device verification code email
|
|
104
|
+
*/
|
|
105
|
+
async sendDeviceCode(email, code, verificationUrl) {
|
|
106
|
+
const subject = 'Your ArchiCore Device Verification Code';
|
|
107
|
+
const html = this.createDeviceCodeTemplate(code, verificationUrl);
|
|
108
|
+
return this.send({
|
|
109
|
+
to: email,
|
|
110
|
+
subject,
|
|
111
|
+
html
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Send email verification code
|
|
116
|
+
*/
|
|
117
|
+
async sendVerificationCode(email, code) {
|
|
118
|
+
const subject = 'Verify Your Email - ArchiCore';
|
|
119
|
+
const html = this.createVerificationCodeTemplate(code);
|
|
120
|
+
return this.send({
|
|
121
|
+
to: email,
|
|
122
|
+
subject,
|
|
123
|
+
html
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Send welcome email after registration
|
|
128
|
+
*/
|
|
129
|
+
async sendWelcome(email, username) {
|
|
130
|
+
const subject = 'Welcome to ArchiCore!';
|
|
131
|
+
const html = this.createWelcomeTemplate(username);
|
|
132
|
+
return this.send({
|
|
133
|
+
to: email,
|
|
134
|
+
subject,
|
|
135
|
+
html
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Create HTML template for device verification code
|
|
140
|
+
*/
|
|
141
|
+
createDeviceCodeTemplate(code, verificationUrl) {
|
|
142
|
+
return `
|
|
143
|
+
<!DOCTYPE html>
|
|
144
|
+
<html lang="en">
|
|
145
|
+
<head>
|
|
146
|
+
<meta charset="UTF-8">
|
|
147
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
148
|
+
<title>ArchiCore Device Verification</title>
|
|
149
|
+
<style>
|
|
150
|
+
body {
|
|
151
|
+
margin: 0;
|
|
152
|
+
padding: 0;
|
|
153
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
154
|
+
background-color: #0a0a0a;
|
|
155
|
+
color: #ffffff;
|
|
156
|
+
}
|
|
157
|
+
.container {
|
|
158
|
+
max-width: 600px;
|
|
159
|
+
margin: 0 auto;
|
|
160
|
+
padding: 40px 20px;
|
|
161
|
+
}
|
|
162
|
+
.header {
|
|
163
|
+
text-align: center;
|
|
164
|
+
margin-bottom: 40px;
|
|
165
|
+
}
|
|
166
|
+
.logo {
|
|
167
|
+
font-size: 32px;
|
|
168
|
+
font-weight: 700;
|
|
169
|
+
background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
|
|
170
|
+
-webkit-background-clip: text;
|
|
171
|
+
-webkit-text-fill-color: transparent;
|
|
172
|
+
background-clip: text;
|
|
173
|
+
margin-bottom: 10px;
|
|
174
|
+
}
|
|
175
|
+
.card {
|
|
176
|
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
|
177
|
+
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
178
|
+
border-radius: 12px;
|
|
179
|
+
padding: 40px;
|
|
180
|
+
text-align: center;
|
|
181
|
+
}
|
|
182
|
+
.title {
|
|
183
|
+
font-size: 24px;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
margin: 0 0 20px 0;
|
|
186
|
+
}
|
|
187
|
+
.description {
|
|
188
|
+
font-size: 16px;
|
|
189
|
+
color: #a0a0a0;
|
|
190
|
+
line-height: 1.6;
|
|
191
|
+
margin: 0 0 30px 0;
|
|
192
|
+
}
|
|
193
|
+
.code-box {
|
|
194
|
+
background: rgba(0, 0, 0, 0.4);
|
|
195
|
+
border: 2px solid #667eea;
|
|
196
|
+
border-radius: 8px;
|
|
197
|
+
padding: 20px;
|
|
198
|
+
margin: 30px 0;
|
|
199
|
+
}
|
|
200
|
+
.code {
|
|
201
|
+
font-size: 36px;
|
|
202
|
+
font-weight: 700;
|
|
203
|
+
letter-spacing: 4px;
|
|
204
|
+
color: #0ea5e9;
|
|
205
|
+
font-family: 'Courier New', monospace;
|
|
206
|
+
}
|
|
207
|
+
.button {
|
|
208
|
+
display: inline-block;
|
|
209
|
+
background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
|
|
210
|
+
color: #ffffff;
|
|
211
|
+
text-decoration: none;
|
|
212
|
+
padding: 14px 32px;
|
|
213
|
+
border-radius: 8px;
|
|
214
|
+
font-weight: 600;
|
|
215
|
+
font-size: 16px;
|
|
216
|
+
margin: 20px 0;
|
|
217
|
+
transition: transform 0.2s;
|
|
218
|
+
}
|
|
219
|
+
.button:hover {
|
|
220
|
+
transform: translateY(-2px);
|
|
221
|
+
}
|
|
222
|
+
.footer {
|
|
223
|
+
text-align: center;
|
|
224
|
+
margin-top: 40px;
|
|
225
|
+
padding-top: 30px;
|
|
226
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
227
|
+
color: #666;
|
|
228
|
+
font-size: 14px;
|
|
229
|
+
}
|
|
230
|
+
.expiry {
|
|
231
|
+
background: rgba(255, 193, 7, 0.1);
|
|
232
|
+
border-left: 3px solid #ffc107;
|
|
233
|
+
padding: 12px;
|
|
234
|
+
margin: 20px 0;
|
|
235
|
+
border-radius: 4px;
|
|
236
|
+
color: #ffc107;
|
|
237
|
+
font-size: 14px;
|
|
238
|
+
}
|
|
239
|
+
</style>
|
|
240
|
+
</head>
|
|
241
|
+
<body>
|
|
242
|
+
<div class="container">
|
|
243
|
+
<div class="header">
|
|
244
|
+
<div class="logo">⚡ ArchiCore</div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="card">
|
|
248
|
+
<h1 class="title">Device Verification Code</h1>
|
|
249
|
+
<p class="description">
|
|
250
|
+
You requested to authorize a new device to access your ArchiCore account.
|
|
251
|
+
Use the verification code below to complete the authorization.
|
|
252
|
+
</p>
|
|
253
|
+
|
|
254
|
+
<div class="code-box">
|
|
255
|
+
<div class="code">${code}</div>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div class="expiry">
|
|
259
|
+
⏱️ This code expires in 10 minutes
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<a href="${verificationUrl}" class="button">
|
|
263
|
+
Verify Device Now
|
|
264
|
+
</a>
|
|
265
|
+
|
|
266
|
+
<p class="description" style="margin-top: 30px; font-size: 14px;">
|
|
267
|
+
Or copy and paste this link into your browser:<br>
|
|
268
|
+
<a href="${verificationUrl}" style="color: #0ea5e9; word-break: break-all;">${verificationUrl}</a>
|
|
269
|
+
</p>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<div class="footer">
|
|
273
|
+
<p>
|
|
274
|
+
If you didn't request this verification, you can safely ignore this email.<br>
|
|
275
|
+
Someone may have entered your email address by mistake.
|
|
276
|
+
</p>
|
|
277
|
+
<p style="margin-top: 20px;">
|
|
278
|
+
© 2026 ArchiCore - AI Software Architect<br>
|
|
279
|
+
<a href="https://archicore.io" style="color: #0ea5e9; text-decoration: none;">archicore.io</a>
|
|
280
|
+
</p>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</body>
|
|
284
|
+
</html>
|
|
285
|
+
`.trim();
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Create HTML template for welcome email
|
|
289
|
+
*/
|
|
290
|
+
createWelcomeTemplate(username) {
|
|
291
|
+
return `
|
|
292
|
+
<!DOCTYPE html>
|
|
293
|
+
<html lang="en">
|
|
294
|
+
<head>
|
|
295
|
+
<meta charset="UTF-8">
|
|
296
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
297
|
+
<title>Welcome to ArchiCore</title>
|
|
298
|
+
<style>
|
|
299
|
+
body {
|
|
300
|
+
margin: 0;
|
|
301
|
+
padding: 0;
|
|
302
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
303
|
+
background-color: #0a0a0a;
|
|
304
|
+
color: #ffffff;
|
|
305
|
+
}
|
|
306
|
+
.container {
|
|
307
|
+
max-width: 600px;
|
|
308
|
+
margin: 0 auto;
|
|
309
|
+
padding: 40px 20px;
|
|
310
|
+
}
|
|
311
|
+
.header {
|
|
312
|
+
text-align: center;
|
|
313
|
+
margin-bottom: 40px;
|
|
314
|
+
}
|
|
315
|
+
.logo {
|
|
316
|
+
font-size: 32px;
|
|
317
|
+
font-weight: 700;
|
|
318
|
+
background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
|
|
319
|
+
-webkit-background-clip: text;
|
|
320
|
+
-webkit-text-fill-color: transparent;
|
|
321
|
+
background-clip: text;
|
|
322
|
+
margin-bottom: 10px;
|
|
323
|
+
}
|
|
324
|
+
.card {
|
|
325
|
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
|
326
|
+
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
327
|
+
border-radius: 12px;
|
|
328
|
+
padding: 40px;
|
|
329
|
+
}
|
|
330
|
+
.title {
|
|
331
|
+
font-size: 28px;
|
|
332
|
+
font-weight: 600;
|
|
333
|
+
margin: 0 0 20px 0;
|
|
334
|
+
text-align: center;
|
|
335
|
+
}
|
|
336
|
+
.description {
|
|
337
|
+
font-size: 16px;
|
|
338
|
+
color: #a0a0a0;
|
|
339
|
+
line-height: 1.6;
|
|
340
|
+
margin: 0 0 30px 0;
|
|
341
|
+
}
|
|
342
|
+
.features {
|
|
343
|
+
list-style: none;
|
|
344
|
+
padding: 0;
|
|
345
|
+
margin: 30px 0;
|
|
346
|
+
}
|
|
347
|
+
.features li {
|
|
348
|
+
padding: 12px 0;
|
|
349
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
350
|
+
}
|
|
351
|
+
.features li:last-child {
|
|
352
|
+
border-bottom: none;
|
|
353
|
+
}
|
|
354
|
+
.feature-icon {
|
|
355
|
+
display: inline-block;
|
|
356
|
+
width: 24px;
|
|
357
|
+
margin-right: 12px;
|
|
358
|
+
}
|
|
359
|
+
.button {
|
|
360
|
+
display: inline-block;
|
|
361
|
+
background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
|
|
362
|
+
color: #ffffff;
|
|
363
|
+
text-decoration: none;
|
|
364
|
+
padding: 14px 32px;
|
|
365
|
+
border-radius: 8px;
|
|
366
|
+
font-weight: 600;
|
|
367
|
+
font-size: 16px;
|
|
368
|
+
margin: 20px 0;
|
|
369
|
+
text-align: center;
|
|
370
|
+
}
|
|
371
|
+
.footer {
|
|
372
|
+
text-align: center;
|
|
373
|
+
margin-top: 40px;
|
|
374
|
+
padding-top: 30px;
|
|
375
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
376
|
+
color: #666;
|
|
377
|
+
font-size: 14px;
|
|
378
|
+
}
|
|
379
|
+
</style>
|
|
380
|
+
</head>
|
|
381
|
+
<body>
|
|
382
|
+
<div class="container">
|
|
383
|
+
<div class="header">
|
|
384
|
+
<div class="logo">⚡ ArchiCore</div>
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<div class="card">
|
|
388
|
+
<h1 class="title">Welcome to ArchiCore, ${username}!</h1>
|
|
389
|
+
<p class="description">
|
|
390
|
+
Thank you for joining ArchiCore - your AI-powered software architect.
|
|
391
|
+
We're excited to have you on board!
|
|
392
|
+
</p>
|
|
393
|
+
|
|
394
|
+
<ul class="features">
|
|
395
|
+
<li><span class="feature-icon">🔍</span> Semantic code search across your entire codebase</li>
|
|
396
|
+
<li><span class="feature-icon">🎯</span> AI-powered impact analysis for code changes</li>
|
|
397
|
+
<li><span class="feature-icon">📊</span> Architecture visualization and insights</li>
|
|
398
|
+
<li><span class="feature-icon">🚀</span> CLI integration for seamless workflow</li>
|
|
399
|
+
<li><span class="feature-icon">⚡</span> Real-time code quality monitoring</li>
|
|
400
|
+
</ul>
|
|
401
|
+
|
|
402
|
+
<div style="text-align: center;">
|
|
403
|
+
<a href="https://archicore.io" class="button">
|
|
404
|
+
Get Started
|
|
405
|
+
</a>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
408
|
+
<p class="description" style="margin-top: 30px; font-size: 14px; text-align: center;">
|
|
409
|
+
Need help? Check out our <a href="https://docs.archicore.io" style="color: #0ea5e9;">documentation</a>
|
|
410
|
+
or reach out to our support team.
|
|
411
|
+
</p>
|
|
412
|
+
</div>
|
|
413
|
+
|
|
414
|
+
<div class="footer">
|
|
415
|
+
<p>
|
|
416
|
+
© 2026 ArchiCore - AI Software Architect<br>
|
|
417
|
+
<a href="https://archicore.io" style="color: #0ea5e9; text-decoration: none;">archicore.io</a> |
|
|
418
|
+
<a href="https://docs.archicore.io" style="color: #0ea5e9; text-decoration: none;">Documentation</a>
|
|
419
|
+
</p>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
</body>
|
|
423
|
+
</html>
|
|
424
|
+
`.trim();
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Create HTML template for email verification code
|
|
428
|
+
*/
|
|
429
|
+
createVerificationCodeTemplate(code) {
|
|
430
|
+
return `
|
|
431
|
+
<!DOCTYPE html>
|
|
432
|
+
<html lang="en">
|
|
433
|
+
<head>
|
|
434
|
+
<meta charset="UTF-8">
|
|
435
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
436
|
+
<title>Verify Your Email - ArchiCore</title>
|
|
437
|
+
<style>
|
|
438
|
+
body {
|
|
439
|
+
margin: 0;
|
|
440
|
+
padding: 0;
|
|
441
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
442
|
+
background-color: #0a0a0a;
|
|
443
|
+
color: #ffffff;
|
|
444
|
+
}
|
|
445
|
+
.container {
|
|
446
|
+
max-width: 500px;
|
|
447
|
+
margin: 0 auto;
|
|
448
|
+
padding: 40px 20px;
|
|
449
|
+
}
|
|
450
|
+
.header {
|
|
451
|
+
text-align: center;
|
|
452
|
+
margin-bottom: 40px;
|
|
453
|
+
}
|
|
454
|
+
.logo {
|
|
455
|
+
font-size: 28px;
|
|
456
|
+
font-weight: 700;
|
|
457
|
+
background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
|
|
458
|
+
-webkit-background-clip: text;
|
|
459
|
+
-webkit-text-fill-color: transparent;
|
|
460
|
+
background-clip: text;
|
|
461
|
+
margin-bottom: 10px;
|
|
462
|
+
}
|
|
463
|
+
.card {
|
|
464
|
+
background: linear-gradient(135deg, rgba(14, 165, 233, 0.1) 0%, rgba(6, 182, 212, 0.1) 100%);
|
|
465
|
+
border: 1px solid rgba(14, 165, 233, 0.3);
|
|
466
|
+
border-radius: 16px;
|
|
467
|
+
padding: 40px;
|
|
468
|
+
text-align: center;
|
|
469
|
+
}
|
|
470
|
+
.title {
|
|
471
|
+
font-size: 24px;
|
|
472
|
+
font-weight: 600;
|
|
473
|
+
margin: 0 0 16px 0;
|
|
474
|
+
color: #f0f4f8;
|
|
475
|
+
}
|
|
476
|
+
.description {
|
|
477
|
+
font-size: 15px;
|
|
478
|
+
color: #94a3b8;
|
|
479
|
+
line-height: 1.6;
|
|
480
|
+
margin: 0 0 32px 0;
|
|
481
|
+
}
|
|
482
|
+
.code-container {
|
|
483
|
+
background: rgba(0, 0, 0, 0.5);
|
|
484
|
+
border: 2px solid #0ea5e9;
|
|
485
|
+
border-radius: 12px;
|
|
486
|
+
padding: 24px;
|
|
487
|
+
margin: 32px 0;
|
|
488
|
+
}
|
|
489
|
+
.code-label {
|
|
490
|
+
font-size: 13px;
|
|
491
|
+
color: #64748b;
|
|
492
|
+
text-transform: uppercase;
|
|
493
|
+
letter-spacing: 1px;
|
|
494
|
+
margin-bottom: 12px;
|
|
495
|
+
}
|
|
496
|
+
.code {
|
|
497
|
+
font-size: 42px;
|
|
498
|
+
font-weight: 700;
|
|
499
|
+
letter-spacing: 8px;
|
|
500
|
+
color: #38bdf8;
|
|
501
|
+
font-family: 'SF Mono', 'Monaco', 'Courier New', monospace;
|
|
502
|
+
user-select: all;
|
|
503
|
+
}
|
|
504
|
+
.expiry {
|
|
505
|
+
background: rgba(6, 182, 212, 0.1);
|
|
506
|
+
border-left: 3px solid #06b6d4;
|
|
507
|
+
padding: 16px;
|
|
508
|
+
margin: 24px 0;
|
|
509
|
+
border-radius: 6px;
|
|
510
|
+
color: #7dd3fc;
|
|
511
|
+
font-size: 14px;
|
|
512
|
+
}
|
|
513
|
+
.footer {
|
|
514
|
+
text-align: center;
|
|
515
|
+
margin-top: 40px;
|
|
516
|
+
padding-top: 30px;
|
|
517
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
518
|
+
color: #475569;
|
|
519
|
+
font-size: 13px;
|
|
520
|
+
}
|
|
521
|
+
.footer a {
|
|
522
|
+
color: #0ea5e9;
|
|
523
|
+
text-decoration: none;
|
|
524
|
+
}
|
|
525
|
+
</style>
|
|
526
|
+
</head>
|
|
527
|
+
<body>
|
|
528
|
+
<div class="container">
|
|
529
|
+
<div class="header">
|
|
530
|
+
<div class="logo">⚡ ArchiCore</div>
|
|
531
|
+
</div>
|
|
532
|
+
|
|
533
|
+
<div class="card">
|
|
534
|
+
<h1 class="title">Verify Your Email</h1>
|
|
535
|
+
<p class="description">
|
|
536
|
+
Enter this verification code to complete your registration
|
|
537
|
+
</p>
|
|
538
|
+
|
|
539
|
+
<div class="code-container">
|
|
540
|
+
<div class="code-label">Verification Code</div>
|
|
541
|
+
<div class="code">${code}</div>
|
|
542
|
+
</div>
|
|
543
|
+
|
|
544
|
+
<div class="expiry">
|
|
545
|
+
⏱️ This code expires in 10 minutes
|
|
546
|
+
</div>
|
|
547
|
+
|
|
548
|
+
<p class="description" style="margin-top: 24px; font-size: 13px;">
|
|
549
|
+
Copy and paste this code into the registration form
|
|
550
|
+
</p>
|
|
551
|
+
</div>
|
|
552
|
+
|
|
553
|
+
<div class="footer">
|
|
554
|
+
<p>
|
|
555
|
+
If you didn't request this code, you can safely ignore this email.
|
|
556
|
+
</p>
|
|
557
|
+
<p style="margin-top: 16px;">
|
|
558
|
+
© 2026 ArchiCore<br>
|
|
559
|
+
<a href="https://archicore.io">archicore.io</a>
|
|
560
|
+
</p>
|
|
561
|
+
</div>
|
|
562
|
+
</div>
|
|
563
|
+
</body>
|
|
564
|
+
</html>
|
|
565
|
+
`.trim();
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Strip HTML tags for plain text version
|
|
569
|
+
*/
|
|
570
|
+
stripHtml(html) {
|
|
571
|
+
return html
|
|
572
|
+
.replace(/<style[^>]*>.*?<\/style>/gis, '')
|
|
573
|
+
.replace(/<[^>]+>/g, '')
|
|
574
|
+
.replace(/\s+/g, ' ')
|
|
575
|
+
.trim();
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Check if email service is enabled
|
|
579
|
+
*/
|
|
580
|
+
isEnabled() {
|
|
581
|
+
return this.enabled;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Export singleton instance
|
|
585
|
+
export const emailService = new EmailService();
|
|
586
|
+
//# sourceMappingURL=email-service.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List of disposable/temporary email domains to block
|
|
3
|
+
* Updated regularly to prevent spam and fake registrations
|
|
4
|
+
*/
|
|
5
|
+
export declare const DISPOSABLE_EMAIL_DOMAINS: Set<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Check if email domain is disposable/temporary
|
|
8
|
+
*/
|
|
9
|
+
export declare function isDisposableEmail(email: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Get user-friendly error message for disposable email
|
|
12
|
+
*/
|
|
13
|
+
export declare function getDisposableEmailError(): string;
|
|
14
|
+
//# sourceMappingURL=disposable-email-domains.d.ts.map
|