Skip to content

Commit c3fb150

Browse files
minor fixes
1 parent b0d597c commit c3fb150

5 files changed

Lines changed: 317 additions & 35 deletions

File tree

version 2/scripts/setup.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,20 @@ async function main() {
170170
run(`echo "${finalJwt}" | wrangler secret put JWT_TOKEN`);
171171
success('JWT_TOKEN set.');
172172

173+
const mailDomain = await ask('Enter your Mail Domain (e.g., example.com):');
174+
if (mailDomain) {
175+
// Update MAIL_DOMAIN in wrangler.toml (it's defined as an env var, not a secret)
176+
wranglerConfig = fs.readFileSync(wranglerPath, 'utf-8');
177+
const updatedConfig = wranglerConfig.replace(
178+
/MAIL_DOMAIN\s*=\s*"[^"]*"/,
179+
`MAIL_DOMAIN = "${mailDomain}"`
180+
);
181+
fs.writeFileSync(wranglerPath, updatedConfig);
182+
success(`MAIL_DOMAIN set to '${mailDomain}' in wrangler.toml.`);
183+
} else {
184+
warn('MAIL_DOMAIN not set. You may need to configure this manually in wrangler.toml.');
185+
}
186+
173187

174188
// 7. Deploy
175189
step('Building and Deploying...');
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import { useState } from 'react';
2+
import { apiFetch } from '@/lib/api';
3+
import { Button } from '@/components/ui/button';
4+
import { Input } from '@/components/ui/input';
5+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
6+
import { Label } from '@/components/ui/label';
7+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
8+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
9+
import { Copy, Mail, RefreshCw, Sparkles } from 'lucide-react';
10+
import toast from 'react-hot-toast';
11+
12+
interface GeneratedEmail {
13+
email: string;
14+
expires: number;
15+
}
16+
17+
export function TempMailGenerator() {
18+
const [generatedEmail, setGeneratedEmail] = useState<string>('');
19+
const [customLocal, setCustomLocal] = useState('');
20+
const [selectedDomain, setSelectedDomain] = useState('gxtend.vip');
21+
const [isGenerating, setIsGenerating] = useState(false);
22+
const [isCreating, setIsCreating] = useState(false);
23+
24+
const handleRandomGenerate = async () => {
25+
setIsGenerating(true);
26+
try {
27+
const data = await apiFetch<GeneratedEmail>('/api/generate?length=8&domainIndex=0');
28+
if (data?.email) {
29+
setGeneratedEmail(data.email);
30+
toast.success('Random email generated!');
31+
}
32+
} catch (error) {
33+
console.error('Failed to generate email', error);
34+
toast.error('Failed to generate email');
35+
} finally {
36+
setIsGenerating(false);
37+
}
38+
};
39+
40+
const handleCustomCreate = async () => {
41+
if (!customLocal.trim()) {
42+
toast.error('Please enter a local part');
43+
return;
44+
}
45+
46+
if (!/^[a-z0-9._-]{1,64}$/i.test(customLocal.trim())) {
47+
toast.error('Invalid format. Use only letters, numbers, dots, hyphens, and underscores');
48+
return;
49+
}
50+
51+
setIsCreating(true);
52+
try {
53+
const data = await apiFetch<GeneratedEmail>('/api/create', {
54+
method: 'POST',
55+
body: JSON.stringify({
56+
local: customLocal.trim(),
57+
domainIndex: 0
58+
})
59+
});
60+
61+
if (data?.email) {
62+
setGeneratedEmail(data.email);
63+
setCustomLocal('');
64+
toast.success('Custom email created!');
65+
}
66+
} catch (error) {
67+
console.error('Failed to create email', error);
68+
toast.error('Failed to create email');
69+
} finally {
70+
setIsCreating(false);
71+
}
72+
};
73+
74+
const handleCopy = () => {
75+
if (generatedEmail) {
76+
navigator.clipboard.writeText(generatedEmail);
77+
toast.success('Email copied to clipboard!');
78+
}
79+
};
80+
81+
return (
82+
<Card>
83+
<CardHeader>
84+
<CardTitle className="flex items-center gap-2">
85+
<Mail className="h-5 w-5" />
86+
Temp Mail Generator
87+
</CardTitle>
88+
<CardDescription>
89+
Generate temporary email addresses for testing or privacy
90+
</CardDescription>
91+
</CardHeader>
92+
<CardContent className="space-y-4">
93+
<Tabs defaultValue="random" className="w-full">
94+
<TabsList className="grid w-full grid-cols-2">
95+
<TabsTrigger value="random">Random</TabsTrigger>
96+
<TabsTrigger value="custom">Custom</TabsTrigger>
97+
</TabsList>
98+
99+
<TabsContent value="random" className="space-y-4">
100+
<div className="space-y-2">
101+
<Label>Generate Random Email</Label>
102+
<p className="text-sm text-muted-foreground">
103+
Click the button below to generate a random temporary email address
104+
</p>
105+
</div>
106+
<Button
107+
onClick={handleRandomGenerate}
108+
disabled={isGenerating}
109+
className="w-full"
110+
>
111+
{isGenerating ? (
112+
<>
113+
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
114+
Generating...
115+
</>
116+
) : (
117+
<>
118+
<Sparkles className="mr-2 h-4 w-4" />
119+
Generate Random Email
120+
</>
121+
)}
122+
</Button>
123+
</TabsContent>
124+
125+
<TabsContent value="custom" className="space-y-4">
126+
<div className="space-y-2">
127+
<Label htmlFor="custom-local">Custom Email Address</Label>
128+
<div className="flex gap-2">
129+
<Input
130+
id="custom-local"
131+
placeholder="yourname"
132+
value={customLocal}
133+
onChange={(e) => setCustomLocal(e.target.value)}
134+
onKeyPress={(e) => {
135+
if (e.key === 'Enter') {
136+
handleCustomCreate();
137+
}
138+
}}
139+
/>
140+
<span className="flex items-center text-muted-foreground">@</span>
141+
<Select value={selectedDomain} onValueChange={setSelectedDomain}>
142+
<SelectTrigger className="w-[180px]">
143+
<SelectValue />
144+
</SelectTrigger>
145+
<SelectContent>
146+
<SelectItem value="gxtend.vip">gxtend.vip</SelectItem>
147+
</SelectContent>
148+
</Select>
149+
</div>
150+
<p className="text-xs text-muted-foreground">
151+
Use letters, numbers, dots, hyphens, and underscores (1-64 characters)
152+
</p>
153+
</div>
154+
<Button
155+
onClick={handleCustomCreate}
156+
disabled={isCreating}
157+
className="w-full"
158+
>
159+
{isCreating ? (
160+
<>
161+
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
162+
Creating...
163+
</>
164+
) : (
165+
<>
166+
<Mail className="mr-2 h-4 w-4" />
167+
Create Custom Email
168+
</>
169+
)}
170+
</Button>
171+
</TabsContent>
172+
</Tabs>
173+
174+
{generatedEmail && (
175+
<div className="mt-4 p-4 bg-muted rounded-lg space-y-2">
176+
<Label className="text-sm font-medium">Generated Email</Label>
177+
<div className="flex items-center gap-2">
178+
<Input
179+
value={generatedEmail}
180+
readOnly
181+
className="font-mono bg-background"
182+
/>
183+
<Button
184+
variant="outline"
185+
size="icon"
186+
onClick={handleCopy}
187+
title="Copy to clipboard"
188+
>
189+
<Copy className="h-4 w-4" />
190+
</Button>
191+
</div>
192+
<p className="text-xs text-muted-foreground">
193+
Click the copy button to copy the email address to your clipboard
194+
</p>
195+
</div>
196+
)}
197+
</CardContent>
198+
</Card>
199+
);
200+
}

version 2/src/pages/dashboard/index.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Mail, Users } from 'lucide-react';
88
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
99
import { UserList } from './UserList';
1010
import { MailboxList } from './MailboxList';
11+
import { TempMailGenerator } from './TempMailGenerator';
1112

1213
export default function Dashboard() {
1314
const { user } = useAuth();
@@ -104,19 +105,22 @@ export default function Dashboard() {
104105
</Card>
105106
</div>
106107

107-
<Card className="col-span-3">
108-
<CardHeader>
109-
<CardTitle>Recent Activity</CardTitle>
110-
<CardDescription>
111-
Overview of system activity.
112-
</CardDescription>
113-
</CardHeader>
114-
<CardContent>
115-
<div className="text-sm text-muted-foreground">
116-
System running normally. Use the tabs above to manage Mailboxes and Users.
117-
</div>
118-
</CardContent>
119-
</Card>
108+
<div className="grid gap-4 md:grid-cols-2">
109+
<TempMailGenerator />
110+
<Card>
111+
<CardHeader>
112+
<CardTitle>Recent Activity</CardTitle>
113+
<CardDescription>
114+
Overview of system activity.
115+
</CardDescription>
116+
</CardHeader>
117+
<CardContent>
118+
<div className="text-sm text-muted-foreground">
119+
System running normally. Use the tabs above to manage Mailboxes and Users.
120+
</div>
121+
</CardContent>
122+
</Card>
123+
</div>
120124
</TabsContent>
121125

122126
{isAdmin && (

version 2/src/pages/mailbox/index.tsx

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ interface EmailSummary {
2020
preview: string;
2121
received_at: string;
2222
is_read?: boolean;
23+
mailbox_address?: string; // For admin all-mailbox view
2324
}
2425

2526
interface EmailDetail extends EmailSummary {
@@ -47,12 +48,22 @@ export default function Mailbox() {
4748
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false);
4849

4950
const fetchEmails = useCallback(async (silent = false) => {
50-
if (!targetMailbox) return;
51+
// Allow admin to fetch all emails when no mailbox specified
52+
const isAdminAllView = !targetMailbox && user?.role === 'admin';
53+
54+
if (!targetMailbox && !isAdminAllView) return;
5155

5256
if (!silent) setIsLoadingList(true);
5357
try {
54-
// Fetch more emails to better support client-side search
55-
const endpoint = `/api/emails?mailbox=${encodeURIComponent(targetMailbox)}&limit=100&offset=0`;
58+
let endpoint;
59+
if (isAdminAllView) {
60+
// Admin view: fetch all emails from all mailboxes
61+
endpoint = `/api/emails?limit=100&offset=0`;
62+
} else {
63+
// Specific mailbox view
64+
endpoint = `/api/emails?mailbox=${encodeURIComponent(targetMailbox!)}&limit=100&offset=0`;
65+
}
66+
5667
const response = await apiFetch<{ results?: EmailSummary[] } | EmailSummary[]>(endpoint);
5768

5869
if (Array.isArray(response)) {
@@ -66,13 +77,14 @@ export default function Mailbox() {
6677
} finally {
6778
if (!silent) setIsLoadingList(false);
6879
}
69-
}, [targetMailbox]);
80+
}, [targetMailbox, user?.role]);
7081

7182
useEffect(() => {
72-
if (targetMailbox) {
83+
const isAdminAllView = !targetMailbox && user?.role === 'admin';
84+
if (targetMailbox || isAdminAllView) {
7385
fetchEmails();
7486
}
75-
}, [fetchEmails, targetMailbox]);
87+
}, [fetchEmails, targetMailbox, user?.role]);
7688

7789
// Auto-refresh logic
7890
useEffect(() => {
@@ -196,6 +208,11 @@ export default function Mailbox() {
196208
{formatDistanceToNow(new Date(email.received_at), { addSuffix: true })}
197209
</span>
198210
</div>
211+
{email.mailbox_address && (
212+
<div className="text-xs text-blue-600 dark:text-blue-400 mb-1">
213+
📬 {email.mailbox_address}
214+
</div>
215+
)}
199216
<div className="text-sm truncate mb-1">{email.subject}</div>
200217
<div className="text-xs text-muted-foreground line-clamp-2 font-normal">
201218
{email.preview}

0 commit comments

Comments
 (0)