Compare commits

...

2 Commits

5 changed files with 79 additions and 31 deletions

View File

@@ -18,7 +18,7 @@ await dbClient.execute(`
CREATE TABLE users ( CREATE TABLE users (
id varchar(20) NOT NULL, id varchar(20) NOT NULL,
name varchar(20) NOT NULL, name varchar(20) NOT NULL,
pin varchar(16) NOT NULL, hash varchar(60) NOT NULL,
email varchar(255) NULL, email varchar(255) NULL,
deleteCode varchar(20) NULL, deleteCode varchar(20) NULL,
PRIMARY KEY (id), PRIMARY KEY (id),

View File

@@ -22,6 +22,7 @@
}, },
"nodeModulesDir": "none", "nodeModulesDir": "none",
"imports": { "imports": {
"@bcrypt": "jsr:@felix/bcrypt",
"@mysql": "https://deno.land/x/mysql@v2.12.1/mod.ts", "@mysql": "https://deno.land/x/mysql@v2.12.1/mod.ts",
"@nanoid": "https://deno.land/x/nanoid@v3.0.0/mod.ts", "@nanoid": "https://deno.land/x/nanoid@v3.0.0/mod.ts",
"@std/http": "jsr:@std/http@1.0.15", "@std/http": "jsr:@std/http@1.0.15",

46
deno.lock generated
View File

@@ -1,17 +1,40 @@
{ {
"version": "5", "version": "5",
"specifiers": { "specifiers": {
"jsr:@denosaurs/plug@^1.1.0": "1.1.0",
"jsr:@felix/bcrypt@*": "1.0.8",
"jsr:@std/cli@^1.0.17": "1.0.17", "jsr:@std/cli@^1.0.17": "1.0.17",
"jsr:@std/encoding@1": "1.0.10",
"jsr:@std/encoding@^1.0.10": "1.0.10", "jsr:@std/encoding@^1.0.10": "1.0.10",
"jsr:@std/fmt@1": "1.0.7",
"jsr:@std/fmt@^1.0.7": "1.0.7", "jsr:@std/fmt@^1.0.7": "1.0.7",
"jsr:@std/fs@1": "1.0.23",
"jsr:@std/html@^1.0.3": "1.0.3", "jsr:@std/html@^1.0.3": "1.0.3",
"jsr:@std/http@1.0.15": "1.0.15", "jsr:@std/http@1.0.15": "1.0.15",
"jsr:@std/internal@^1.0.12": "1.0.13",
"jsr:@std/media-types@^1.1.0": "1.1.0", "jsr:@std/media-types@^1.1.0": "1.1.0",
"jsr:@std/net@^1.0.4": "1.0.4", "jsr:@std/net@^1.0.4": "1.0.4",
"jsr:@std/path@1": "1.0.9",
"jsr:@std/path@^1.0.9": "1.0.9", "jsr:@std/path@^1.0.9": "1.0.9",
"jsr:@std/path@^1.1.4": "1.1.4",
"jsr:@std/streams@^1.0.9": "1.0.9" "jsr:@std/streams@^1.0.9": "1.0.9"
}, },
"jsr": { "jsr": {
"@denosaurs/plug@1.1.0": {
"integrity": "eb2f0b7546c7bca2000d8b0282c54d50d91cf6d75cb26a80df25a6de8c4bc044",
"dependencies": [
"jsr:@std/encoding@1",
"jsr:@std/fmt@1",
"jsr:@std/fs",
"jsr:@std/path@1"
]
},
"@felix/bcrypt@1.0.8": {
"integrity": "59c41160fc027882479c512db5d53792c4d91aadcd49467c85caa2f1679046f2",
"dependencies": [
"jsr:@denosaurs/plug"
]
},
"@std/cli@1.0.17": { "@std/cli@1.0.17": {
"integrity": "e15b9abe629e17be90cc6216327f03a29eae613365f1353837fa749aad29ce7b" "integrity": "e15b9abe629e17be90cc6216327f03a29eae613365f1353837fa749aad29ce7b"
}, },
@@ -21,6 +44,13 @@
"@std/fmt@1.0.7": { "@std/fmt@1.0.7": {
"integrity": "2a727c043d8df62cd0b819b3fb709b64dd622e42c3b1bb817ea7e6cc606360fb" "integrity": "2a727c043d8df62cd0b819b3fb709b64dd622e42c3b1bb817ea7e6cc606360fb"
}, },
"@std/fs@1.0.23": {
"integrity": "3ecbae4ce4fee03b180fa710caff36bb5adb66631c46a6460aaad49515565a37",
"dependencies": [
"jsr:@std/internal",
"jsr:@std/path@^1.1.4"
]
},
"@std/html@1.0.3": { "@std/html@1.0.3": {
"integrity": "7a0ac35e050431fb49d44e61c8b8aac1ebd55937e0dc9ec6409aa4bab39a7988" "integrity": "7a0ac35e050431fb49d44e61c8b8aac1ebd55937e0dc9ec6409aa4bab39a7988"
}, },
@@ -28,15 +58,18 @@
"integrity": "435a4934b4e196e82a8233f724da525f7b7112f3566502f28815e94764c19159", "integrity": "435a4934b4e196e82a8233f724da525f7b7112f3566502f28815e94764c19159",
"dependencies": [ "dependencies": [
"jsr:@std/cli", "jsr:@std/cli",
"jsr:@std/encoding", "jsr:@std/encoding@^1.0.10",
"jsr:@std/fmt", "jsr:@std/fmt@^1.0.7",
"jsr:@std/html", "jsr:@std/html",
"jsr:@std/media-types", "jsr:@std/media-types",
"jsr:@std/net", "jsr:@std/net",
"jsr:@std/path", "jsr:@std/path@^1.0.9",
"jsr:@std/streams" "jsr:@std/streams"
] ]
}, },
"@std/internal@1.0.13": {
"integrity": "2f9546691d4ac2d32859c82dff284aaeac980ddeca38430d07941e7e288725c0"
},
"@std/media-types@1.1.0": { "@std/media-types@1.1.0": {
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4" "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
}, },
@@ -46,6 +79,12 @@
"@std/path@1.0.9": { "@std/path@1.0.9": {
"integrity": "260a49f11edd3db93dd38350bf9cd1b4d1366afa98e81b86167b4e3dd750129e" "integrity": "260a49f11edd3db93dd38350bf9cd1b4d1366afa98e81b86167b4e3dd750129e"
}, },
"@std/path@1.1.4": {
"integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
"dependencies": [
"jsr:@std/internal"
]
},
"@std/streams@1.0.9": { "@std/streams@1.0.9": {
"integrity": "a9d26b1988cdd7aa7b1f4b51e1c36c1557f3f252880fa6cc5b9f37078b1a5035" "integrity": "a9d26b1988cdd7aa7b1f4b51e1c36c1557f3f252880fa6cc5b9f37078b1a5035"
} }
@@ -120,6 +159,7 @@
}, },
"workspace": { "workspace": {
"dependencies": [ "dependencies": [
"jsr:@felix/bcrypt@*",
"jsr:@std/http@1.0.15" "jsr:@std/http@1.0.15"
] ]
} }

53
mod.ts
View File

@@ -1,3 +1,4 @@
import { hash, verify } from '@bcrypt';
import { customAlphabet } from '@nanoid'; import { customAlphabet } from '@nanoid';
import { STATUS_CODE, STATUS_TEXT, StatusCode } from '@std/http/status'; import { STATUS_CODE, STATUS_TEXT, StatusCode } from '@std/http/status';
@@ -42,7 +43,7 @@ Deno.serve({ port: config.api.port }, async (req) => {
} else if (path.startsWith('/home/')) { } else if (path.startsWith('/home/')) {
// SSR "home page" // SSR "home page"
const userId = path.replace('/home/', ''); const userId = path.replace('/home/', '');
const userMatch = await dbClient.query('SELECT name FROM users WHERE id = ?', [userId]).catch(() => { const userMatch = await dbClient.query('SELECT name FROM users WHERE BINARY id = ?', [userId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return buildPage("Couldn't read DB. Please try again."); if (failed) return buildPage("Couldn't read DB. Please try again.");
@@ -52,7 +53,7 @@ Deno.serve({ port: config.api.port }, async (req) => {
} else if (path.startsWith('/read/')) { } else if (path.startsWith('/read/')) {
const planId = path.replace('/read/', ''); const planId = path.replace('/read/', '');
const plans = await dbClient.query('SELECT name, folder, data FROM plans WHERE id = ? AND deleted = 0', [planId]).catch(() => { const plans = await dbClient.query('SELECT name, folder, data FROM plans WHERE BINARY id = ? AND deleted = 0', [planId]).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.");
@@ -61,14 +62,14 @@ Deno.serve({ port: config.api.port }, async (req) => {
return genericResponse(STATUS_CODE.OK, JSON.stringify(plans[0])); return genericResponse(STATUS_CODE.OK, JSON.stringify(plans[0]));
} else if (path.startsWith('/list/')) { } else if (path.startsWith('/list/')) {
const userId = path.replace('/list/', ''); const userId = path.replace('/list/', '');
const userMatch = await dbClient.query('SELECT id FROM users WHERE id = ?', [userId]).catch(() => { const userMatch = await dbClient.query('SELECT id FROM users WHERE BINARY id = ?', [userId]).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 (!userMatch.length) return genericResponse(STATUS_CODE.NotFound, 'User ID does not exist.'); if (!userMatch.length) return genericResponse(STATUS_CODE.NotFound, 'User ID does not exist.');
const plans = await dbClient const plans = await dbClient
.query('SELECT id, name, folder, lastUpdated FROM plans WHERE ownerId = ? AND deleted = 0 ORDER BY folder ASC,name ASC', [userId]) .query('SELECT id, name, folder, lastUpdated FROM plans WHERE BINARY ownerId = ? AND deleted = 0 ORDER BY folder ASC,name ASC', [userId])
.catch(() => { .catch(() => {
failed = true; failed = true;
}); });
@@ -81,7 +82,7 @@ Deno.serve({ port: config.api.port }, async (req) => {
return genericResponse(STATUS_CODE.OK, JSON.stringify(plans)); return genericResponse(STATUS_CODE.OK, JSON.stringify(plans));
} else if (path.startsWith('/export/')) { } else if (path.startsWith('/export/')) {
const userId = path.replace('/export/', ''); const userId = path.replace('/export/', '');
const userMatch = await dbClient.query('SELECT id FROM users WHERE id = ?', [userId]).catch(() => { const userMatch = await dbClient.query('SELECT id FROM users WHERE BINARY id = ?', [userId]).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.");
@@ -93,7 +94,7 @@ Deno.serve({ port: config.api.port }, async (req) => {
} 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 BINARY name = ?', [body.name]).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.");
@@ -101,12 +102,13 @@ Deno.serve({ port: config.api.port }, async (req) => {
if (userNameMatches.length === 0) { if (userNameMatches.length === 0) {
if (body.name.length < 4 || body.name.length > 20) if (body.name.length < 4 || body.name.length > 20)
return genericResponse(STATUS_CODE.BadRequest, `Name too ${body.name.length < 4 ? 'short' : 'long'}.`); return genericResponse(STATUS_CODE.BadRequest, `Name too ${body.name.length < 4 ? 'short' : 'long'}.`);
if (body.pin.length < 4 || body.pin.length > 16) return genericResponse(STATUS_CODE.BadRequest, `PIN too ${body.pin.length < 4 ? 'short' : 'long'}.`); if (body.pin.length < 4) 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.'); if (body.email.length > 255) return genericResponse(STATUS_CODE.BadRequest, 'Email too long.');
const id = nanoid(); const id = nanoid();
const pinHash = await hash(body.pin);
await dbClient.execute('INSERT INTO users(id,name,pin,email) values(?,?,?,?)', [id, body.name, body.pin, body.email]).catch(() => { await dbClient.execute('INSERT INTO users(id,name,hash,email) values(?,?,?,?)', [id, body.name, pinHash, body.email]).catch(() => {
failed = true; failed = true;
}); });
@@ -121,11 +123,12 @@ Deno.serve({ port: config.api.port }, async (req) => {
} else { } else {
const body = await req.json(); 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, hash, email, deleteCode FROM users WHERE BINARY name = ?', [body.name]).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. Remember name is case sensitive.');
if (!(await verify(body.pin, loginMatch[0].hash))) 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;
@@ -152,28 +155,28 @@ Deno.serve({ port: config.api.port }, async (req) => {
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 BINARY id = ?', [planId]).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 (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.'); 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."); if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET deleted = 0, lastUpdated = ? WHERE id = ?', [new Date(), planId]).catch(() => { await dbClient.execute('UPDATE plans SET deleted = 0, lastUpdated = ? WHERE BINARY id = ?', [new Date(), planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
return genericResponse(STATUS_CODE.OK, 'Plan restored.'); return genericResponse(STATUS_CODE.OK, 'Plan restored.');
} else if (path.startsWith('/update/')) { } else if (path.startsWith('/update/')) {
const planId = path.replace('/update/', ''); const planId = path.replace('/update/', '');
const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => { const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE BINARY id = ?', [planId]).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 (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.'); 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."); if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET data = ?, lastUpdated = ? WHERE id = ?', [body.data, new Date(), planId]).catch(() => { await dbClient.execute('UPDATE plans SET data = ?, lastUpdated = ? WHERE BINARY id = ?', [body.data, new Date(), planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
@@ -182,14 +185,14 @@ Deno.serve({ port: config.api.port }, async (req) => {
if (body.planName.trim().length > 200) return genericResponse(STATUS_CODE.BadRequest, 'Name too long.'); if (body.planName.trim().length > 200) return genericResponse(STATUS_CODE.BadRequest, 'Name too long.');
const planId = path.replace('/rename/', ''); const planId = path.replace('/rename/', '');
const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => { const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE BINARY id = ?', [planId]).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 (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.'); 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."); if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET name = ?, lastUpdated = ? WHERE id = ?', [body.planName, new Date(), planId]).catch(() => { await dbClient.execute('UPDATE plans SET name = ?, lastUpdated = ? WHERE BINARY id = ?', [body.planName, new Date(), planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
@@ -198,14 +201,14 @@ Deno.serve({ port: config.api.port }, async (req) => {
if (body.folder.trim().length > 200) return genericResponse(STATUS_CODE.BadRequest, 'Folder name too long.'); if (body.folder.trim().length > 200) return genericResponse(STATUS_CODE.BadRequest, 'Folder name too long.');
const planId = path.replace('/move/', ''); const planId = path.replace('/move/', '');
const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => { const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE BINARY id = ?', [planId]).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 (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.'); 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."); if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET folder = ?, lastUpdated = ? WHERE id = ?', [body.folder, new Date(), planId]).catch(() => { await dbClient.execute('UPDATE plans SET folder = ?, lastUpdated = ? WHERE BINARY id = ?', [body.folder, new Date(), planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
@@ -215,12 +218,12 @@ Deno.serve({ port: config.api.port }, async (req) => {
case 'DELETE': case 'DELETE':
if (path === '/unenroll') { if (path === '/unenroll') {
if (!hasEmail || body.deleteCode.trim() === deleteCode) { if (!hasEmail || body.deleteCode.trim() === deleteCode) {
await dbClient.execute('DELETE FROM plans WHERE ownerId = ?', [id]).catch(() => { await dbClient.execute('DELETE FROM plans WHERE BINARY 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 delete plans.");
await dbClient.execute('DELETE FROM users WHERE id = ?', [id]).catch(() => { await dbClient.execute('DELETE FROM users WHERE BINARY id = ?', [id]).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 delete user.");
@@ -228,7 +231,7 @@ Deno.serve({ port: config.api.port }, async (req) => {
return genericResponse(STATUS_CODE.OK, 'Deleted user and plans.'); return genericResponse(STATUS_CODE.OK, 'Deleted user and plans.');
} else if (hasEmail && !deleteCode) { } else if (hasEmail && !deleteCode) {
const newDeleteCode = nanoid(); const newDeleteCode = nanoid();
await dbClient.execute('UPDATE users SET deleteCode = ? WHERE id = ?', [newDeleteCode, id]).catch(() => { await dbClient.execute('UPDATE users SET deleteCode = ? WHERE BINARY id = ?', [newDeleteCode, id]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't set deleteCode."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't set deleteCode.");
@@ -279,21 +282,21 @@ Deno.serve({ port: config.api.port }, async (req) => {
} }
} else if (path.startsWith('/delete/')) { } else if (path.startsWith('/delete/')) {
const planId = path.replace('/delete/', ''); const planId = path.replace('/delete/', '');
const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE id = ?', [planId]).catch(() => { const planMatch = await dbClient.query('SELECT ownerId FROM plans WHERE BINARY id = ?', [planId]).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 (!planMatch.length) return genericResponse(STATUS_CODE.NotFound, 'Plan ID does not exist.'); 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."); if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
await dbClient.execute('UPDATE plans SET deleted = 1, lastUpdated = ? WHERE id = ?', [new Date(), planId]).catch(() => { await dbClient.execute('UPDATE plans SET deleted = 1, lastUpdated = ? WHERE BINARY id = ?', [new Date(), planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");
return genericResponse(STATUS_CODE.OK, 'Plan deleted.'); return genericResponse(STATUS_CODE.OK, 'Plan deleted.');
} else if (path.startsWith('/perm-delete/')) { } else if (path.startsWith('/perm-delete/')) {
const planId = path.replace('/perm-delete/', ''); const planId = path.replace('/perm-delete/', '');
const planMatch = await dbClient.query('SELECT ownerId, deleted FROM plans WHERE id = ?', [planId]).catch(() => { const planMatch = await dbClient.query('SELECT ownerId, deleted FROM plans WHERE BINARY id = ?', [planId]).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.");
@@ -301,7 +304,7 @@ Deno.serve({ port: config.api.port }, async (req) => {
if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan."); if (planMatch[0].ownerId !== id) return genericResponse(STATUS_CODE.Forbidden, "You don't own this plan.");
if (!planMatch[0].deleted) return genericResponse(STATUS_CODE.Forbidden, 'Plan must be marked as deleted to perm delete.'); if (!planMatch[0].deleted) return genericResponse(STATUS_CODE.Forbidden, 'Plan must be marked as deleted to perm delete.');
await dbClient.execute('DELETE FROM plans WHERE id = ? AND deleted = 1', [planId]).catch(() => { await dbClient.execute('DELETE FROM plans WHERE BINARY id = ? AND deleted = 1', [planId]).catch(() => {
failed = true; failed = true;
}); });
if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB."); if (failed) return genericResponse(STATUS_CODE.InternalServerError, "Couldn't update DB.");

View File

@@ -21,14 +21,18 @@ export default async (userId: string, userName: string) => {
let failed = false; let failed = false;
const plans: Plan[] = await dbClient const plans: Plan[] = await dbClient
.query('SELECT id, name, folder, lastUpdated FROM plans WHERE ownerId = ? AND deleted = 0 GROUP BY folder,name,id ORDER BY folder ASC,name ASC', [userId]) .query('SELECT id, name, folder, lastUpdated FROM plans WHERE BINARY ownerId = ? AND deleted = 0 GROUP BY folder,name,id ORDER BY folder ASC,name ASC', [
userId,
])
.catch((e) => { .catch((e) => {
failed = true; failed = true;
}); });
if (failed) return "Couldn't read DB."; if (failed) return "Couldn't read DB.";
const deletedPlans: Plan[] = await dbClient const deletedPlans: Plan[] = await dbClient
.query('SELECT id, name, folder, lastUpdated FROM plans WHERE ownerId = ? AND deleted = 1 GROUP BY folder,name,id ORDER BY folder ASC,name ASC', [userId]) .query('SELECT id, name, folder, lastUpdated FROM plans WHERE BINARY ownerId = ? AND deleted = 1 GROUP BY folder,name,id ORDER BY folder ASC,name ASC', [
userId,
])
.catch(() => { .catch(() => {
failed = true; failed = true;
}); });