로직 설명
toolRoute.post('/shortten', async (c) => {
const formData = await c.req.formData();
const url = formData.get('url');
// 1-1. 검증
if (!url) {
return c.json({ success: false, message: 'URL을 입력해주세요.' }, 400);
}
// 1-2. 검증
if (!url || !url.match(/^https?:\/\//)) {
return c.json({ success: false, message: 'http[s]:// 로 시작하는 올바른 URL을 입력해주세요.', result: null }, 400);
}
// 1-3. 검증
if (!url.match(/\./)) {
return c.json({ success: false, message: '올바른 도메인을 입력해주세요.', result: null }, 400);
}
const token = formData.get('cf-turnstile-response');
// 2. Cloudflare turnstile 토큰 자체가 없으면 바로 차단
if (!token) {
return c.json({ success: false, message: '캡차 인증이 필요합니다.' }, 400);
}
// 3. Cloudflare 캡챠 인증
const verifyRes = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: c.env.CF_TURNSTILE_SECRET,
response: token,
remoteip: c.req.header('CF-Connecting-IP'),
}),
}
);
const urls = new Urls(c);
// 4. 해당 URL 을 가진 데이터가 있는지 검색
const urlData = await urls.selectOne({ url });
let result = null;
// 5-1.있으면 INSERT 건너뛰고 바로 리턴
if(urlData) {
result = urlData;
// 5.2 없으면 URL 단축 후 INSERT
} else {
const outcome = await verifyRes.json();
if (!outcome.success) {
console.error('Turnstile 검증 실패:', outcome['error-codes']);
return c.json({ success: false, message: `캡차 검증에 실패했습니다.` }, 403);
}
let max = await urls.selectMax('id');
if (!max || max === 0) {
max = 0;
}
const number = String(max + 1);
const shortCode = StringUtil.textToBase62(number);
result = await urls.insert({ code: shortCode, url: url });
}
return c.json({ success: true, result: `https://${c.env.HOST}/s/${result.code}` });
});