Skip to content

Commit 2ee5c23

Browse files
Send email (#2)
* fixes * fix-2 * fix-3 * fix-4 * fix-4
1 parent fcf28da commit 2ee5c23

10 files changed

Lines changed: 215 additions & 159 deletions

File tree

scripts/setup.js

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ const error = (msg) => {
3232

3333
const run = (cmd, options = {}) => {
3434
try {
35+
// Prepare options for execSync
36+
const execOptions = { ...options };
37+
if (options.input) {
38+
execOptions.input = options.input;
39+
delete execOptions.input; // Remote input from options passed to execSync if it's not supported directly in the spread (though it is for some node versions, let's be safe)
40+
}
41+
42+
// If providing input, we must rely on the provided stdio or default to pipe for stdin
43+
// execSync with input handles stdin automatically if not overridden to something incompatible
3544
const result = execSync(cmd, { stdio: 'pipe', encoding: 'utf-8', ...options });
3645
return result ? result.trim() : null;
3746
} catch (e) {
@@ -157,8 +166,13 @@ async function main() {
157166
// Check if secrets likely exist (rudimentary check, or just prompt to overwrite)
158167
const adminPassword = await ask('Enter a secure Admin Password:');
159168
if (adminPassword) {
160-
run(`echo "${adminPassword}" | wrangler secret put ADMIN_PASSWORD`);
161-
success('ADMIN_PASSWORD set.');
169+
// Use input option to pass password to stdin, avoiding echo and potential shell issues
170+
try {
171+
run('wrangler secret put ADMIN_PASSWORD', { input: adminPassword, stdio: ['pipe', 'pipe', 'inherit'] });
172+
success('ADMIN_PASSWORD set.');
173+
} catch (e) {
174+
error(`Failed to set ADMIN_PASSWORD: ${e.message}`);
175+
}
162176
}
163177

164178
const jwtToken = await ask('Enter a random JWT Token (or press Enter to generate one):');
@@ -167,8 +181,22 @@ async function main() {
167181
finalJwt = crypto.randomBytes(32).toString('hex');
168182
console.log(`Generated JWT Token: ${finalJwt}`);
169183
}
170-
run(`echo "${finalJwt}" | wrangler secret put JWT_TOKEN`);
171-
success('JWT_TOKEN set.');
184+
try {
185+
run('wrangler secret put JWT_TOKEN', { input: finalJwt, stdio: ['pipe', 'pipe', 'inherit'] });
186+
success('JWT_TOKEN set.');
187+
} catch (e) {
188+
error(`Failed to set JWT_TOKEN: ${e.message}`);
189+
}
190+
191+
const resendApiKey = await ask('Enter your Resend API Key (optional, for sending emails):');
192+
if (resendApiKey) {
193+
try {
194+
run('wrangler secret put RESEND_API_KEY', { input: resendApiKey, stdio: ['pipe', 'pipe', 'inherit'] });
195+
success('RESEND_API_KEY set.');
196+
} catch (e) {
197+
error(`Failed to set RESEND_API_KEY: ${e.message}`);
198+
}
199+
}
172200

173201
const mailDomain = await ask('Enter your Mail Domain (e.g., example.com):');
174202
if (mailDomain) {
@@ -184,6 +212,34 @@ async function main() {
184212
warn('MAIL_DOMAIN not set. You may need to configure this manually in wrangler.toml.');
185213
}
186214

215+
let finalAdminName = 'admin';
216+
const adminName = await ask('Enter a custom Admin Username (default: admin):');
217+
if (adminName && adminName.trim() !== '' && adminName.trim() !== 'admin') {
218+
finalAdminName = adminName.trim();
219+
wranglerConfig = fs.readFileSync(wranglerPath, 'utf-8');
220+
221+
// Check if ADMIN_NAME already exists
222+
if (wranglerConfig.includes('ADMIN_NAME')) {
223+
const updatedConfig = wranglerConfig.replace(
224+
/ADMIN_NAME\s*=\s*"[^"]*"/,
225+
`ADMIN_NAME = "${finalAdminName}"`
226+
);
227+
fs.writeFileSync(wranglerPath, updatedConfig);
228+
} else {
229+
// Append to [vars] section
230+
if (wranglerConfig.includes('[vars]')) {
231+
const updatedConfig = wranglerConfig.replace(
232+
/\[vars\]/,
233+
`[vars]\nADMIN_NAME = "${finalAdminName}"`
234+
);
235+
fs.writeFileSync(wranglerPath, updatedConfig);
236+
} else {
237+
fs.appendFileSync(wranglerPath, `\n[vars]\nADMIN_NAME = "${finalAdminName}"\n`);
238+
}
239+
}
240+
success(`ADMIN_NAME set to '${finalAdminName}' in wrangler.toml.`);
241+
}
242+
187243

188244
// 7. Deploy
189245
step('Building and Deploying...');
@@ -196,7 +252,7 @@ async function main() {
196252

197253
console.log(`\n${colors.green}${colors.bright}✅ Deployment Complete!${colors.reset}`);
198254
console.log(`\nYour app should be live. Check the URL above.`);
199-
console.log(`Admin User: admin`);
255+
console.log(`Admin User: ${finalAdminName}`);
200256
console.log(`Admin Password: (hidden)`);
201257

202258
} catch (e) {

src/components/shared/ComposeEmail.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,17 @@ export function ComposeEmail({ onClose, initialValues }: {
2525
return;
2626
}
2727

28+
let fromAddress = user?.mailboxAddress || '';
29+
if (!fromAddress && user?.username && user?.mailDomain) {
30+
fromAddress = `${user.username}@${user.mailDomain}`;
31+
}
32+
// Fallback if no domain available (though should be)
33+
if (!fromAddress) {
34+
fromAddress = user?.username || '';
35+
}
36+
2837
const payload = {
29-
from: user?.mailboxAddress || user?.username || '', // Fallback or handle correctly
38+
from: fromAddress,
3039
to,
3140
subject,
3241
[isHtml ? 'html' : 'text']: body,

src/context/AuthContext.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface User {
99
mailbox_limit?: number;
1010
id?: number;
1111
userId?: number;
12+
mailDomain?: string;
1213
}
1314

1415
interface AuthContextType {
@@ -25,6 +26,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
2526
const [user, setUser] = useState<User | null>(null);
2627
const [isLoading, setIsLoading] = useState(true);
2728

29+
2830
const checkSession = async () => {
2931
try {
3032
const data = await apiFetch<User>('/api/session');
@@ -33,8 +35,14 @@ export function AuthProvider({ children }: { children: ReactNode }) {
3335
} else {
3436
setUser(null);
3537
}
36-
} catch {
37-
setUser(null);
38+
} catch (error: any) {
39+
// 401 Unauthorized is expected if not logged in
40+
if (error.status === 401) {
41+
setUser(null);
42+
} else {
43+
console.error('Session check failed:', error);
44+
setUser(null);
45+
}
3846
} finally {
3947
setIsLoading(false);
4048
}

src/lib/api.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,25 @@ export async function apiFetch<T = unknown>(
1515
};
1616

1717
const response = await fetch(endpoint, {
18+
credentials: 'include',
1819
...options,
1920
headers,
2021
});
2122

22-
const data = await response.json().catch(() => ({}));
23+
const text = await response.text();
24+
let data: any;
25+
try {
26+
data = JSON.parse(text);
27+
} catch {
28+
// If not JSON, use text as message
29+
data = { message: text };
30+
}
2331

2432
if (!response.ok) {
25-
throw new Error(data.message || response.statusText || 'API Error');
33+
const error: any = new Error(data.message || response.statusText || `API Error: ${response.status}`);
34+
error.status = response.status;
35+
error.statusText = response.statusText;
36+
throw error;
2637
}
2738

2839
return data as T;

src/main.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { StrictMode } from 'react'
21
import { createRoot } from 'react-dom/client'
32
import './index.css'
43
import App from './App.tsx'
54

65
createRoot(document.getElementById('root')!).render(
7-
<StrictMode>
86
<App />
9-
</StrictMode>,
107
)

0 commit comments

Comments
 (0)