prepared queries
This commit is contained in:
parent
d10a9e2bde
commit
0348b07325
3 changed files with 90 additions and 39 deletions
20
bun.lock
20
bun.lock
|
@ -76,21 +76,21 @@
|
|||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
||||
|
||||
"@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@0.18.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lJanO+V1JcV4QpfkFo/4L8qyjoL4jqSrOgA4bsf2JK0QH1w+bpQ4979dDF0HXsp1uJoyYVtkVuT8wP4C9TdJzg=="],
|
||||
"@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Ei8wLh65Th/si5EY6mfQIXVpdXbJWOoh56FaxxPgVxTeJaj3NHUIlxICHkvTZ5dz8bnOFcbS/+9MaW8Qkzfm9g=="],
|
||||
|
||||
"@oxlint/darwin-x64": ["@oxlint/darwin-x64@0.18.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-UiGJh67KSoS2RyEQQCjp5qKHBcu1vk/5kaF4f9db4JJmatpyEuqDjuvE6GBqhqWAKh064IvSVubQtvRp0v1M6w=="],
|
||||
"@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-dbdtQ+rJTUb4jFKTzV+j08yYcR8lZssLF10n7MggK/jI7pBtoQN04cupzYdkxOWSy6uDXjDmWYFDIqlTqV7zOg=="],
|
||||
|
||||
"@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@0.18.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-3CsWnXoAB26GxlQzPiJKzd6lY0Z/S3Or6oAF/AwbS+Bn2hiB73Kt+BFOcFp5m3EOEDdGYHfRwIn2y1wUf76diA=="],
|
||||
"@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-71wy9zMxsAeRhCFQjUkDLT8N5tm10L5FxNxsUcEsezgM187X9tPGP1gwlFpYig7F+bg2X1dijFuTA/FSe0YpKg=="],
|
||||
|
||||
"@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@0.18.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-gzVgf1GT+WHsujlj4MtQUvzILJ+SlYPib+xLx0aFIHE5jrnd6ZUuT+Nxrnn/cLA+f+9cPmDD7JByVksPFHhT2w=="],
|
||||
"@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UbD4+2k7aGZOFtKK/yeESX7Fv0w9gQbcjrjr1HGY7QOYg7XlFlqzycZdPS6XbAuKA5oOXFpafaYOD4AyX3p2AA=="],
|
||||
|
||||
"@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@0.18.0", "", { "os": "linux", "cpu": "x64" }, "sha512-D94AgHk4qZt2v658OXZP13Re/Q1/hRQhMdOy+pzeih9POcTY31NOHmBInyI2OjZzUMT3JzUKc+efAmX7rlDJFg=="],
|
||||
"@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-0NXWqsm65I3VaLgADW4y9r7Pwurqgs2fr1lqoTyTIlidD18LQ3UMAWp8NzBPMCYzw8c/rTgOzsFf0gLtxzMtwg=="],
|
||||
|
||||
"@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@0.18.0", "", { "os": "linux", "cpu": "x64" }, "sha512-B3kGrEE68F7qvyVaH2ihYB00E/iQOsi2SN+NeDht9FyUiftZRtIYlvZ/VE8l8yxXX/TFtwhed3bg2/IvaJ8Tpg=="],
|
||||
"@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-AY1NLnVQI+tBeuaB8KCriWfiD6O1zZFAQHphRDcZiqSz4mauNq9FFuffW0N9RSR9hYttGr0UVdQ6eK72RhzOYg=="],
|
||||
|
||||
"@oxlint/win32-arm64": ["@oxlint/win32-arm64@0.18.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-d+q8Vkhy4va7UZAETov1BWDA5G9XItY6HR7j57Uso5xfqVmFvnk2+L0LH9Z/PPFwNNR/37vBB364bJ4V6v/DlQ=="],
|
||||
"@oxlint/win32-arm64": ["@oxlint/win32-arm64@1.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-X9y2KAdoqT/jy/sITGDZNMJHJAmhDhofItBnCf2DWS1HPakdtCAKGX9KMx6SivTbtPn1+JpZgfHn4Y7rNMvujQ=="],
|
||||
|
||||
"@oxlint/win32-x64": ["@oxlint/win32-x64@0.18.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gMxC4XrhnR5XRtjqhIvkPI1yyinwkSjp7VgeOTf4mDk0xwYROwg/mSWnKfDn7tXUKoWQLDsSfAp/SS8uSAQ9Bg=="],
|
||||
"@oxlint/win32-x64": ["@oxlint/win32-x64@1.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-x2eQwZCfRUi6GG0lhRuC54O6TK2uW7UbIvERh83vPi0ftd+rtGUuJauNdyC+pPx+iwFToFVet43/5MBMu4bMWg=="],
|
||||
|
||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.2", "", {}, "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog=="],
|
||||
|
||||
|
@ -172,7 +172,7 @@
|
|||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"oxlint": ["oxlint@0.18.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "0.18.0", "@oxlint/darwin-x64": "0.18.0", "@oxlint/linux-arm64-gnu": "0.18.0", "@oxlint/linux-arm64-musl": "0.18.0", "@oxlint/linux-x64-gnu": "0.18.0", "@oxlint/linux-x64-musl": "0.18.0", "@oxlint/win32-arm64": "0.18.0", "@oxlint/win32-x64": "0.18.0" }, "bin": { "oxlint": "bin/oxlint", "oxc_language_server": "bin/oxc_language_server" } }, "sha512-Dni48uC00HoBn63I1aFXS7ta7whJOMK5yBc/PQjQVQ1EWutHpcExhWO73XcOeljKUNsEkNWF/yTUFOIN6HbZiA=="],
|
||||
"oxlint": ["oxlint@1.0.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.0.0", "@oxlint/darwin-x64": "1.0.0", "@oxlint/linux-arm64-gnu": "1.0.0", "@oxlint/linux-arm64-musl": "1.0.0", "@oxlint/linux-x64-gnu": "1.0.0", "@oxlint/linux-x64-musl": "1.0.0", "@oxlint/win32-arm64": "1.0.0", "@oxlint/win32-x64": "1.0.0" }, "bin": { "oxlint": "bin/oxlint", "oxc_language_server": "bin/oxc_language_server" } }, "sha512-yyeryHnd21wPBLBEF4Uf8hvzJlftrIGHxyUaqFaP2JYiZ9cbiColygZhrezvv/Z/aThCmYu3j6iJMxlVPxNt6g=="],
|
||||
|
||||
"pioppo": ["pioppo@1.2.1", "", { "dependencies": { "dettle": "^1.0.5", "when-exit": "^2.1.4" } }, "sha512-1oErGVWD6wFDPmrJWEY1Cj2p829UGT6Fw9OItYFxLkWtBjCvQSMC8wA5IcAR5ms/6gqiY8pnJvIV/+/Imyobew=="],
|
||||
|
||||
|
@ -206,7 +206,7 @@
|
|||
|
||||
"stubborn-fs": ["stubborn-fs@1.2.5", "", {}, "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="],
|
||||
"tailwindcss": ["tailwindcss@4.1.9", "", {}, "sha512-anBZRcvfNMsQdHB9XSGzAtIQWlhs49uK75jfkwrqjRUbjt4d7q9RE1wR1xWyfYZhLFnFX4ahWp88Au2lcEw5IQ=="],
|
||||
|
||||
"tiny-bin": ["tiny-bin@1.11.1", "", { "dependencies": { "ansi-purge": "^1.0.1", "fast-string-width": "^1.1.0", "get-current-package": "^1.0.1", "tiny-colors": "^2.2.2", "tiny-levenshtein": "^1.0.1", "tiny-parse-argv": "^2.8.2", "tiny-updater": "^3.5.3" } }, "sha512-UFC5EwtmCkFshKOBgXZzNFJsHpZrtbWZ/jQj+pwoIGUUbmenlQGGVDOwVqVOuG1nTxICSd+GLp3b+j7dUKZr2Q=="],
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"bun-plugin-tailwind": "^0.0.15",
|
||||
"drizzle-kit": "^0.30.6",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
"tailwindcss": "^4.1.8"
|
||||
"tailwindcss": "^4.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
|
|
107
src/index.ts
107
src/index.ts
|
@ -1,11 +1,11 @@
|
|||
import homepage from '@routes/index.html';
|
||||
import { drizzleDB } from "@server/db";
|
||||
import { entry } from "@server/db/schema";
|
||||
import { drizzleDB } from '@server/db';
|
||||
import { entry } from '@server/db/schema';
|
||||
import favicon from '@static/favicon.png';
|
||||
import robotsTxt from '@static/robots.txt';
|
||||
import sitemapTxt from '@static/sitemap.txt';
|
||||
import { env } from "bun";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import { env } from 'bun';
|
||||
import { desc, eq, sql } from 'drizzle-orm';
|
||||
|
||||
const development = env.NODE_ENV !== 'production';
|
||||
|
||||
|
@ -26,6 +26,58 @@ function etagResponse(etag: string, cacheControl?: string) {
|
|||
let entriesETag = '';
|
||||
let entriesNotModified = false;
|
||||
|
||||
const fetchEntries = drizzleDB
|
||||
.select({
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
href: entry.href,
|
||||
finished: entry.finished,
|
||||
updated_at: entry.updatedAt,
|
||||
})
|
||||
.from(entry)
|
||||
.orderBy(desc(entry.updatedAt))
|
||||
.prepare();
|
||||
|
||||
const entryExists = drizzleDB
|
||||
.select({ id: entry.id })
|
||||
.from(entry)
|
||||
.where(eq(entry.name, sql.placeholder('name')))
|
||||
.prepare();
|
||||
|
||||
const insertEntry = drizzleDB
|
||||
.insert(entry)
|
||||
.values({
|
||||
name: sql.placeholder('name'),
|
||||
href: sql.placeholder('href'),
|
||||
})
|
||||
.prepare();
|
||||
|
||||
const updateEntry = drizzleDB
|
||||
.update(entry)
|
||||
.set({
|
||||
name: sql.placeholder('name') as any,
|
||||
href: sql.placeholder('href') as any,
|
||||
})
|
||||
.where(eq(entry.id, sql.placeholder('id')))
|
||||
.prepare();
|
||||
|
||||
const fetchEntryById = drizzleDB
|
||||
.select()
|
||||
.from(entry)
|
||||
.where(eq(entry.id, sql.placeholder('id')))
|
||||
.prepare();
|
||||
|
||||
const deleteEntry = drizzleDB
|
||||
.delete(entry)
|
||||
.where(eq(entry.id, sql.placeholder('id')))
|
||||
.prepare();
|
||||
|
||||
const updateEntryFinished = drizzleDB
|
||||
.update(entry)
|
||||
.set({ finished: sql.placeholder('finished') as any })
|
||||
.where(eq(entry.id, sql.placeholder('id')))
|
||||
.prepare();
|
||||
|
||||
Bun.serve({
|
||||
routes: {
|
||||
'/': homepage,
|
||||
|
@ -46,16 +98,13 @@ Bun.serve({
|
|||
}
|
||||
|
||||
entriesNotModified = true;
|
||||
const entries = await drizzleDB
|
||||
.select({ id: entry.id, name: entry.name, href: entry.href, finished: entry.finished, updated_at: entry.updatedAt })
|
||||
.from(entry)
|
||||
.orderBy(desc(entry.updatedAt));
|
||||
const entries = await fetchEntries.execute();
|
||||
|
||||
const body = JSON.stringify(entries);
|
||||
entriesETag = getEtag(body);
|
||||
|
||||
return new Response(body, {
|
||||
headers: { 'Content-Type': 'application/json', 'ETag': entriesETag }
|
||||
headers: { 'Content-Type': 'application/json', ETag: entriesETag },
|
||||
});
|
||||
},
|
||||
async PUT(req) {
|
||||
|
@ -64,23 +113,27 @@ Bun.serve({
|
|||
const body = await getEntryFromReq(req);
|
||||
if (!body) return new Response('Invalid data', { status: 400 });
|
||||
|
||||
const result = await drizzleDB.select({ id: entry.id }).from(entry).where(eq(entry.name, body.name));
|
||||
const result = await entryExists.execute({ name: body.name });
|
||||
|
||||
let created = true;
|
||||
|
||||
if (result.length === 0) {
|
||||
await drizzleDB.insert(entry).values(body).execute();
|
||||
await insertEntry.execute(body);
|
||||
} else if (result.length === 1) {
|
||||
const row = result[0] as NonNullable<typeof result[number]>;
|
||||
const row = result[0] as NonNullable<(typeof result)[number]>;
|
||||
created = false;
|
||||
await drizzleDB.update(entry).set(body).where(eq(entry.id, row.id)).execute();
|
||||
await updateEntry.execute({
|
||||
id: row.id,
|
||||
name: body.name,
|
||||
href: body.href,
|
||||
});
|
||||
} else {
|
||||
return new Response('Invalid data, multiple matches?', { status: 400 });
|
||||
}
|
||||
|
||||
entriesNotModified = false;
|
||||
return new Response(JSON.stringify({ created }), {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -91,10 +144,10 @@ Bun.serve({
|
|||
const id = Number.parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) return new Response('Invalid id', { status: 400 });
|
||||
|
||||
const result = await drizzleDB.select().from(entry).where(eq(entry.id, id));
|
||||
const result = await fetchEntryById.execute({ id });
|
||||
|
||||
return new Response(JSON.stringify(result), {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
async PUT(req) {
|
||||
|
@ -106,13 +159,15 @@ Bun.serve({
|
|||
const body = await getEntryFromReq(req);
|
||||
if (!body) return new Response('Invalid data', { status: 400 });
|
||||
|
||||
await drizzleDB.update(entry).set(body).where(eq(entry.id, id)).execute();
|
||||
|
||||
let created = false;
|
||||
await updateEntry.execute({
|
||||
id,
|
||||
name: body.name,
|
||||
href: body.href,
|
||||
});
|
||||
|
||||
entriesNotModified = false;
|
||||
return new Response(JSON.stringify({ created: false }), {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
async DELETE(req) {
|
||||
|
@ -121,7 +176,7 @@ Bun.serve({
|
|||
const id = Number.parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) return new Response('Invalid id', { status: 400 });
|
||||
|
||||
await drizzleDB.delete(entry).where(eq(entry.id, id)).execute();
|
||||
await deleteEntry.execute({ id });
|
||||
|
||||
entriesNotModified = false;
|
||||
return new Response('OK');
|
||||
|
@ -135,11 +190,7 @@ Bun.serve({
|
|||
if (Number.isNaN(id)) return new Response('Invalid id', { status: 400 });
|
||||
const finished = req.params.finished === 'true';
|
||||
|
||||
await drizzleDB
|
||||
.update(entry)
|
||||
.set({ finished: finished })
|
||||
.where(eq(entry.id, id))
|
||||
.execute();
|
||||
await updateEntryFinished.execute({ id, finished });
|
||||
|
||||
entriesNotModified = false;
|
||||
return new Response('OK');
|
||||
|
@ -159,7 +210,7 @@ console.log('Server started on port:', env.PORT ? Number.parseInt(env.PORT, 10)
|
|||
|
||||
async function getEntryFromReq(req: Request) {
|
||||
const json = await req.json();
|
||||
const body = json as { name: string, href: string }
|
||||
const body = json as { name: string; href: string };
|
||||
if (!body.name || !body.href || typeof body.name !== 'string' || typeof body.href !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
@ -180,7 +231,7 @@ function isAuthenticated(req: Request) {
|
|||
}
|
||||
|
||||
const unauthorizedHeaders = new Headers({
|
||||
'WWW-Authenticate': `Bearer realm='sign', error="invalid_request"`
|
||||
'WWW-Authenticate': `Bearer realm='sign', error="invalid_request"`,
|
||||
});
|
||||
|
||||
function unauthorizedResp() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue