我目前正在利用 svelteKit 创建一个网页主页。
我正在创建一个系统,当用户输入
/typo
路径时,他们会填写页面上的表单,当他们按下 Submit
按钮时,它会创建并通过 Googlemail 发送电子邮件。但是,在我的本地环境(开发服务器)中,电子邮件发送正常,但部署后,我收到 400 Bad Request
错误。
你能根据我的代码和截图告诉我问题是什么吗?
我已基于node.js将部署环境设置为
adapter-node
,构建后,我使用index.js
在build
文件夹中运行pm2
。
为什么在部署在我的开发环境(localhost)中运行良好的功能后会出现此问题?
我附上了书面代码的图片,错误消息控制台。
//svelte.config.js
import adapter from '@sveltejs/adapter-node';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
host: '0.0.0.0',
port: 5173,
}),
}
};
export default config;
// src/hooks.js
export async function handle({ request, resolve }) {
const response = await resolve(request);
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', '*');
response.headers.set('Access-Control-Allow-Credentials', 'true');
return response;
}
// src/lib/server/sendEmail.js
import nodemailer from "nodemailer";
import { GMAIL_ADDRESS, GMAIL_PASS } from "$env/static/private";
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: GMAIL_ADDRESS,
pass: GMAIL_PASS,
},
tls: {
rejectUnauthorized: false,
},
});
const sendEmail = async (subject, content) => {
try {
const info = await transporter.sendMail({
from: GMAIL_ADDRESS,
to: GMAIL_ADDRESS,
subject: subject,
html: content,
});
console.log("Email sent: ", info.messageId);
} catch (error) {
console.error("sendEmail error: ", error);
throw error;
}
};
export default sendEmail;
// src/routes/typo/+server.js
import upload from '$lib/server/upload.js';
import { json, error } from '@sveltejs/kit';
import sendEmail from '$lib/server/sendEmail.js';
import crypto from 'crypto';
import { CAPTCHA_SALT } from '$env/static/private';
export async function POST({ request, cookies }) {
const data = await request.formData();
const captchaText = data.get('captchaText');
const hash = data.get('hash');
let userHash = crypto.createHash('md5').update(captchaText + CAPTCHA_SALT).digest('hex');
if (hash !== userHash) {
throw error(400, 'Captcha verification failed');
}
const name = data.get('name');
const bookTitle = data.get('bookTitle');
const email = data.get('email');
const phone = data.get('phone');
const inquiry = data.get('inquiry');
const content =
`<p>이름: ${name}</p>
<p>이메일: ${email}</p>
<p>연락처: ${phone}</p>
<p>책 제목: ${bookTitle}</p>
<p style="white-space: pre-line;">오탈자 내용: ${inquiry}</p>
`
try {
await sendEmail(
`[오타 제보] ${name} 님 | ${bookTitle}`, // 메일 제목
content,
email
);
} catch (err) {
console.error(err);
throw error(500, 'Failed to send email');
}
return json({ message: 'ok' });
}
<!--src/routes/typo/+page.svelte-->
<script>
export let data;
import BookSearch from "../../components/BookSearch.svelte";
import ContentHeading from "../../components/ContentHeading.svelte";
import SubmitButton from "../../components/SubmitButton.svelte";
import TitleSection from "../../components/TitleSection.svelte";
let captchaText = '';
let books = data.books;
let bookId = null;
let selectedBook = null;
let name = '';
let phone = '';
let email = '';
let inquiry = '';
const onSelectBook = (id) => {
bookId = id;
selectedBook = books.find(book => book.id === id);
}
const submit = async () => {
if (!confirm('제출하시겠습니까?')) return;
if (!name) { alert('이름을 입력하세요.'); return; }
if (!email) { alert('이메일을 입력하세요.'); return; }
let emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(email)) { alert('이메일 형식이 올바르지 않습니다.'); return; }
if (!phone) { alert('연락처를 입력하세요.'); return; }
if (!bookId) { alert('도서를 선택하세요.'); return; }
if (!captchaText) { alert('자동입력방지 문자를 입력하세요.'); return; }
const formData = new FormData();
formData.append('name', name);
formData.append('email', email);
formData.append('phone', phone);
formData.append('bookTitle', selectedBook.attributes.title);
formData.append('inquiry', inquiry);
formData.append('captchaText', captchaText);
formData.append('hash', data.hash);
const res = await fetch('/typo', {
method: 'POST',
body: formData,
});
if (res.status === 200) {
alert('제출되었습니다.');
} else {
let errorText = (await res.json()).message;
alert(`제출에 실패했습니다.\n${errorText}`);
}
}
</script>
<TitleSection title="오탈자 제보" />
<div class="about-mission-area pb-70">
<div class="container">
<ContentHeading name="오탈자 제보" />
<div class="row">
<div class="col-lg-12 col-md-12">
<div class="container" style="padding:0px">
<form>
<table class="table" >
<tbody>
<tr class="table-row">
<td class="bg-gray-3 header-text">이름</td>
<td><input bind:value={name} type="text" class="form-control" id="name" placeholder="이름을 입력하세요"></td>
</tr>
<tr class="table-row">
<td class="bg-gray-3 header-text">이메일</td>
<td><input bind:value={email} type="email" class="form-control" id="email" placeholder="이메일 주소를 입력하세요"></td>
</tr>
<tr class="table-row">
<td class="bg-gray-3 header-text">연락처</td>
<td><input bind:value={phone} type="tel" class="form-control" id="phone" placeholder="연락처를 입력하세요"></td>
</tr>
<tr class="table-row">
<td class="bg-gray-3 header-text">도서명</td>
<td class="book-search">
<div class="book-title">
<span>{selectedBook ? selectedBook.attributes.title : "책을 선택해주세요."}</span>
</div>
<BookSearch books={books} onSelect={onSelectBook}/>
</td>
</tr>
<tr class="table-row">
<td class="bg-gray-3 header-text">오탈자 내용</td>
<td><textarea bind:value={inquiry} class="form-control" id="exampleFormControlTextarea1" rows="8"></textarea></td>
</tr>
</tbody>
</table>
<!--captcha-->
<div class="d-flex flex-column align-items-center mb-3">
{@html data.captcha}
<input bind:value={captchaText} type="text" class="form-control mt-2" id="captcha" placeholder="위의 문자를 입력하세요">
</div>
<SubmitButton onClick={submit} text="제출"/>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.book-search {
display: flex;
flex-direction: column;
}
#captcha {
max-width: 260px;
}
.table-row {
padding-bottom: 10px !important;
border-bottom: 5px solid white !important;
font-family: 'Nanum Gothic', sans-serif !important;
font-weight: 700 !important;
}
.header-text {
text-align: left;
vertical-align: middle !important;
font-size: 1rem;
width: 150px;
}
</style>
任何帮助将不胜感激。
检查是否是在部署的服务器上使用 smtp 的端口转发问题,但一切正常。
检查是否是环境变量的问题,但是在开发环境下运行正常。
我尝试直接在请求 URL 中的“/typo”中指定服务器域。 但没有成功。
我尝试在评论中添加尽可能多的细节,但似乎最好写在这里。首先,我不认为这是 SvelteKit 的问题。更像是 PM2 配置问题。
要检查是否不存在,请尝试运行以下命令:
PORT=YOURPORT ORIGIN=http://website.com node index.js
如果它没有引发任何 CORS 错误,那就明白了!如果不是,它可能确实来自 Sveltekit。
首先停止pm2,并禁用你的应用程序。
pm2 stop index.js && pm2 disable [portId]
在您的应用程序文件夹中添加配置文件。
{
"apps" : [
{
"name": "from",
"script": "./production/index.js", <- your index.js file location
"node_args": [
],
"autorestart": false,
"exec_interpreter": "node",
"exec_mode": "cluster",
"instances": 1,
"env": {
"NODE_ENV": "development"
},
"env_production": {
"ORIGIN": "https://yourwebsite.com", <- ORIGIN
"PORT": "PORT" <- Remember ""
}
}
]
}
(这是我在生产服务器上使用的文件。您可能需要调整一些设置)
启动PM2
pm2 start .server_config.json --env production
记住
--env production
。检查它是否有效。另外,不要向 Access-Control-Allow-Origin 添加任何包罗万象的内容。任何人都可以滥用您的表单和 Gmail API。