wrap whole thing in a try catch to try to be safe

This commit is contained in:
Ean Milligan
2026-04-09 16:58:15 -04:00
parent 6e4c448ba0
commit 495698f33e

269
mod.ts
View File

@@ -20,161 +20,166 @@ const genericResponse = (status: StatusCode, customText = '') =>
new Response(customText || STATUS_TEXT[status], { status: status, statusText: STATUS_TEXT[status] }); new Response(customText || STATUS_TEXT[status], { status: status, statusText: STATUS_TEXT[status] });
Deno.serve({ port: config.api.port }, async (req) => { Deno.serve({ port: config.api.port }, async (req) => {
const urlPath = req.url.split('?')[0] ?? ''; try {
let rawPath = (urlPath.split('api')[1] ?? '').trim(); const urlPath = req.url.split('?')[0] ?? '';
if (rawPath.endsWith('/')) rawPath = rawPath.slice(0, -1); let rawPath = (urlPath.split('api')[1] ?? '').trim();
const path = rawPath; if (rawPath.endsWith('/')) rawPath = rawPath.slice(0, -1);
console.log(urlPath, path); const path = rawPath;
console.log(urlPath, path);
let failed = false; let failed = false;
if (req.method === 'GET') { if (req.method === 'GET') {
// handle all gets // handle all gets
} else if (req.method === 'POST' && path === '/enroll') { } else if (req.method === 'POST' && path === '/enroll') {
const body = await req.json(); const body = await req.json();
const userNameMatches = await dbClient.query('SELECT name FROM users WHERE name = ?', [body.name]).catch(() => { const userNameMatches = await dbClient.query('SELECT name FROM users WHERE name = ?', [body.name]).catch(() => {
failed = true;
});
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (userNameMatches.length === 0) {
if (body.name.length < 4 || body.name.length > 20) return genericResponse(STATUS_CODE.BadRequest, `Name too ${body.name.length < 4 ? 'short' : 'long'}.`);
if (body.pin.length < 4 || body.pin.length > 20) return genericResponse(STATUS_CODE.BadRequest, `PIN too ${body.pin.length < 4 ? 'short' : 'long'}.`);
if (body.email.length > 255) return genericResponse(STATUS_CODE.BadRequest, `Email too long.`);
const id = nanoid();
await dbClient.execute('INSERT INTO users(id,name,pin,email) values(?,?,?,?)', [id, body.name, body.pin, body.email]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (failed) { if (userNameMatches.length === 0) {
return genericResponse(STATUS_CODE.InternalServerError, "Couldn't write DB."); if (body.name.length < 4 || body.name.length > 20)
return genericResponse(STATUS_CODE.BadRequest, `Name too ${body.name.length < 4 ? 'short' : 'long'}.`);
if (body.pin.length < 4 || body.pin.length > 20) return genericResponse(STATUS_CODE.BadRequest, `PIN too ${body.pin.length < 4 ? 'short' : 'long'}.`);
if (body.email.length > 255) return genericResponse(STATUS_CODE.BadRequest, `Email too long.`);
const id = nanoid();
await dbClient.execute('INSERT INTO users(id,name,pin,email) values(?,?,?,?)', [id, body.name, body.pin, body.email]).catch(() => {
failed = true;
});
if (failed) {
return genericResponse(STATUS_CODE.InternalServerError, "Couldn't write DB.");
} else {
return genericResponse(STATUS_CODE.OK, JSON.stringify({ id }));
}
} else { } else {
return genericResponse(STATUS_CODE.OK, JSON.stringify({ id })); return genericResponse(STATUS_CODE.BadRequest, 'Username Taken.');
} }
} else { } else {
return genericResponse(STATUS_CODE.BadRequest, 'Username Taken.'); const body = await req.json();
}
} else {
const body = await req.json();
const loginMatch = await dbClient.query('SELECT id, email, deleteCode FROM users WHERE name = ? AND pin = ?', [body.name, body.pin]).catch(() => { const loginMatch = await dbClient.query('SELECT id, email, deleteCode FROM users WHERE name = ? AND pin = ?', [body.name, body.pin]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (loginMatch.length === 0) return genericResponse(STATUS_CODE.Forbidden, 'Invalid name/PIN combination.'); if (loginMatch.length === 0) return genericResponse(STATUS_CODE.Forbidden, 'Invalid name/PIN combination.');
const id = loginMatch[0].id; const id = loginMatch[0].id;
const email = loginMatch[0].email; const email = loginMatch[0].email;
const hasEmail = email.length > 0; const hasEmail = email.length > 0;
const deleteCode = loginMatch[0].deleteCode; const deleteCode = loginMatch[0].deleteCode;
switch (req.method) { switch (req.method) {
case 'POST': case 'POST':
if (path === '/auth') { if (path === '/auth') {
return genericResponse(STATUS_CODE.OK, JSON.stringify({ id, hasEmail })); return genericResponse(STATUS_CODE.OK, JSON.stringify({ id, hasEmail }));
} }
break; break;
case 'PUT': case 'PUT':
if (path.startsWith('/undelete/')) { if (path.startsWith('/undelete/')) {
const planId = path.replace('/undelete/', ''); const planId = path.replace('/undelete/', '');
const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => { const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => {
failed = true;
});
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.');
if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET deleted = 0 WHERE id = ?', [planId]).catch(() => {
failed = true;
});
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
return genericResponse(STATUS_CODE.OK, 'Plan deleted.');
}
break;
case 'DELETE':
if (path === '/unenroll') {
if (!hasEmail || body.deleteCode.trim() === deleteCode) {
await dbClient.execute('DELETE FROM plans WHERE ownerId = ?', [id]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't delete plans."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.');
if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('DELETE FROM users WHERE id = ?', [id]).catch(() => { await dbClient.execute('UPDATE plans SET deleted = 0 WHERE id = ?', [planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't delete user."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
return genericResponse(STATUS_CODE.OK, 'Plan deleted.');
return genericResponse(STATUS_CODE.OK, 'Deleted user and plans.'); }
} else if (hasEmail && !deleteCode) { break;
const newDeleteCode = nanoid(); case 'DELETE':
await dbClient.execute('UPDATE users SET deleteCode = ? WHERE id = ?', [newDeleteCode, id]).catch(() => { if (path === '/unenroll') {
failed = true; if (!hasEmail || body.deleteCode.trim() === deleteCode) {
}); await dbClient.execute('DELETE FROM plans WHERE ownerId = ?', [id]).catch(() => {
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't set deleteCode.");
const nowDT = new Date().getTime();
if (zohoAuthExpireDT < nowDT) {
const getNewAuthToken = await fetch(
`https://accounts.zoho.com/oauth/v2/token?client_id=${config.email.clientId}&client_secret=${config.email.clientSecret}&grant_type=client_credentials&scope=ZohoMail.messages.CREATE`,
{ method: 'POST' },
).catch(() => {
failed = true; failed = true;
}); });
if (failed || !getNewAuthToken) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't get auth token."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't delete plans.");
const newAuthToken = await getNewAuthToken.json(); await dbClient.execute('DELETE FROM users WHERE id = ?', [id]).catch(() => {
zohoHeaders.set('Authorization', `Zoho-oauthtoken ${newAuthToken.access_token}`); failed = true;
zohoAuthExpireDT = nowDT + newAuthToken.expires_in * 1000 - 600000; });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't delete user.");
return genericResponse(STATUS_CODE.OK, 'Deleted user and plans.');
} else if (hasEmail && !deleteCode) {
const newDeleteCode = nanoid();
await dbClient.execute('UPDATE users SET deleteCode = ? WHERE id = ?', [newDeleteCode, id]).catch(() => {
failed = true;
});
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't set deleteCode.");
const nowDT = new Date().getTime();
if (zohoAuthExpireDT < nowDT) {
const getNewAuthToken = await fetch(
`https://accounts.zoho.com/oauth/v2/token?client_id=${config.email.clientId}&client_secret=${config.email.clientSecret}&grant_type=client_credentials&scope=ZohoMail.messages.CREATE`,
{ method: 'POST' },
).catch(() => {
failed = true;
});
if (failed || !getNewAuthToken) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't get auth token.");
const newAuthToken = await getNewAuthToken.json();
zohoHeaders.set('Authorization', `Zoho-oauthtoken ${newAuthToken.access_token}`);
zohoAuthExpireDT = nowDT + newAuthToken.expires_in * 1000 - 600000;
}
const sendEmailReq = await fetch(`https://mail.zoho.com/api/accounts/${config.email.accountId}/messages`, {
method: 'POST',
headers: zohoHeaders,
body: JSON.stringify({
fromAddress: config.email.address,
toAddress: email,
subject: 'XIVPlan+DB Delete Code',
content: `Notice: account deletion is permanent and will delete all plans saved under your account.<br/><br/>Please use the following Delete Code to delete your account:<br/><br/>${newDeleteCode}`,
}),
}).catch(() => {
failed = true;
});
if (failed || !sendEmailReq) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't send email.");
const sentEmail = await sendEmailReq.json();
if (sentEmail.status.code !== 200) return genericResponse(STATUS_CODE.InternalServerError, 'Failed to send email.');
const maskedEmail = `${email.slice(0, 2)}***@***${email.slice(-5)}`;
fetch(config.discordWebhook, {
method: 'POST',
headers: discordHeaders,
body: JSON.stringify({ content: `Delete code email has been sent to \`${maskedEmail}\`.` }),
}).catch(() => {});
return genericResponse(STATUS_CODE.PreconditionFailed, `Please resubmit with the confirmation code emailed to "${maskedEmail}".`);
} else if (hasEmail && body.deleteCode !== deleteCode) {
return genericResponse(STATUS_CODE.BadRequest, 'Invalid delete code.');
} else {
return genericResponse(STATUS_CODE.InternalServerError, 'How are you here?');
} }
} else if (path.startsWith('/delete/')) {
const sendEmailReq = await fetch(`https://mail.zoho.com/api/accounts/${config.email.accountId}/messages`, { const planId = path.replace('/delete/', '');
method: 'POST', const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => {
headers: zohoHeaders,
body: JSON.stringify({
fromAddress: config.email.address,
toAddress: email,
subject: 'XIVPlan+DB Delete Code',
content: `Notice: account deletion is permanent and will delete all plans saved under your account.<br/><br/>Please use the following Delete Code to delete your account:<br/><br/>${newDeleteCode}`,
}),
}).catch(() => {
failed = true; failed = true;
}); });
if (failed || !sendEmailReq) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't send email."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.');
if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
const sentEmail = await sendEmailReq.json(); await dbClient.execute('UPDATE plans SET deleted = 1 WHERE id = ?', [planId]).catch(() => {
if (sentEmail.status.code !== 200) return genericResponse(STATUS_CODE.InternalServerError, 'Failed to send email.'); failed = true;
});
const maskedEmail = `${email.slice(0, 2)}***@***${email.slice(-5)}`; if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
fetch(config.discordWebhook, { return genericResponse(STATUS_CODE.OK, 'Plan deleted.');
method: 'POST',
headers: discordHeaders,
body: JSON.stringify({ content: `Delete code email has been sent to \`${maskedEmail}\`.` }),
}).catch(() => {});
return genericResponse(STATUS_CODE.PreconditionFailed, `Please resubmit with the confirmation code emailed to "${maskedEmail}".`);
} else if (hasEmail && body.deleteCode !== deleteCode) {
return genericResponse(STATUS_CODE.BadRequest, 'Invalid delete code.');
} else {
return genericResponse(STATUS_CODE.InternalServerError, 'How are you here?');
} }
} else if (path.startsWith('/delete/')) { break;
const planId = path.replace('/delete/', ''); }
const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => {
failed = true;
});
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't read DB.");
if (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.');
if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET deleted = 1 WHERE id = ?', [planId]).catch(() => {
failed = true;
});
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
return genericResponse(STATUS_CODE.OK, 'Plan deleted.');
}
break;
} }
}
return genericResponse(STATUS_CODE.NotImplemented); return genericResponse(STATUS_CODE.NotImplemented);
} catch (_e) {
return genericResponse(STATUS_CODE.InternalServerError);
}
}); });