diff --git a/biome.json b/biome.json index 49542c2..463b379 100644 --- a/biome.json +++ b/biome.json @@ -12,6 +12,11 @@ "enabled": true, "indentStyle": "tab" }, + "css": { + "parser": { + "tailwindDirectives": true + } + }, "linter": { "enabled": false, "rules": { diff --git a/bun.lock b/bun.lock index 2ab1014..31abd6e 100644 --- a/bun.lock +++ b/bun.lock @@ -11,6 +11,7 @@ "import": "^0.0.6", }, "devDependencies": { + "@biomejs/biome": "^2.4.4", "@sveltejs/adapter-auto": "^7.0.0", "@sveltejs/adapter-cloudflare": "^7.2.6", "@sveltejs/kit": "^2.49.1", @@ -26,6 +27,24 @@ }, }, "packages": { + "@biomejs/biome": ["@biomejs/biome@2.4.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.4", "@biomejs/cli-darwin-x64": "2.4.4", "@biomejs/cli-linux-arm64": "2.4.4", "@biomejs/cli-linux-arm64-musl": "2.4.4", "@biomejs/cli-linux-x64": "2.4.4", "@biomejs/cli-linux-x64-musl": "2.4.4", "@biomejs/cli-win32-arm64": "2.4.4", "@biomejs/cli-win32-x64": "2.4.4" }, "bin": { "biome": "bin/biome" } }, "sha512-tigwWS5KfJf0cABVd52NVaXyAVv4qpUXOWJ1rxFL8xF1RVoeS2q/LK+FHgYoKMclJCuRoCWAPy1IXaN9/mS61Q=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jZ+Xc6qvD6tTH5jM6eKX44dcbyNqJHssfl2nnwT6vma6B1sj7ZLTGIk6N5QwVBs5xGN52r3trk5fgd3sQ9We9A=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dh1a/+W+SUCXhEdL7TiX3ArPTFCQKJTI1mGncZNWfO+6suk+gYA4lNyJcBB+pwvF49uw0pEbUS49BgYOY4hzUg=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-V/NFfbWhsUU6w+m5WYbBenlEAz8eYnSqRMDMAW3K+3v0tYVkNyZn8VU0XPxk/lOqNXLSCCrV7FmV/u3SjCBShg=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+sPAXq3bxmFwhVFJnSwkSF5Rw2ZAJMH3MF6C9IveAEOdSpgajPhoQhbbAK12SehN9j2QrHpk4J/cHsa/HqWaYQ=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.4", "", { "os": "linux", "cpu": "x64" }, "sha512-R4+ZCDtG9kHArasyBO+UBD6jr/FcFCTH8QkNTOCu0pRJzCWyWC4EtZa2AmUZB5h3e0jD7bRV2KvrENcf8rndBg=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gGvFTGpOIQDb5CQ2VC0n9Z2UEqlP46c4aNgHmAMytYieTGEcfqhfCFnhs6xjt0S3igE6q5GLuIXtdQt3Izok+g=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-trzCqM7x+Gn832zZHgr28JoYagQNX4CZkUZhMUac2YxvvyDRLJDrb5m9IA7CaZLlX6lTQmADVfLEKP1et1Ma4Q=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.4", "", { "os": "win32", "cpu": "x64" }, "sha512-gnOHKVPFAAPrpoPt2t+Q6FZ7RPry/FDV3GcpU53P3PtLNnQjBmKyN2Vh/JtqXet+H4pme8CC76rScwdjDcT1/A=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.11.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260115.0" }, "optionalPeers": ["workerd"] }, "sha512-z3hxFajL765VniNPGV0JRStZolNz63gU3B3AktwoGdDlnQvz5nP+Ah4RL04PONlZQjwmDdGHowEStJ94+RsaJg=="], diff --git a/package.json b/package.json index 6469232..0af2101 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,11 @@ "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "biome format --write src/" }, "devDependencies": { + "@biomejs/biome": "^2.4.4", "@sveltejs/adapter-auto": "^7.0.0", "@sveltejs/adapter-cloudflare": "^7.2.6", "@sveltejs/kit": "^2.49.1", diff --git a/src/hooks.server.ts b/src/hooks.server.ts index a0d7d20..8151acd 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -2,15 +2,15 @@ import type { Handle } from "@sveltejs/kit"; import { verifySession } from "$lib"; export const handle: Handle = async ({ event, resolve }) => { - const sessionCookie = event.cookies.get("session"); - const secret = event.platform?.env?.SESSION_SECRET; + const sessionCookie = event.cookies.get("session"); + const secret = event.platform?.env?.SESSION_SECRET; - if (sessionCookie && secret) { - const isValid = await verifySession(sessionCookie, secret); - event.locals.user = isValid ? { authenticated: true } : null; - } else { - event.locals.user = null; - } + if (sessionCookie && secret) { + const isValid = await verifySession(sessionCookie, secret); + event.locals.user = isValid ? { authenticated: true } : null; + } else { + event.locals.user = null; + } - return resolve(event); + return resolve(event); }; diff --git a/src/lib/components/ProgressiveImage.svelte b/src/lib/components/ProgressiveImage.svelte index 3e02f9c..57f3d1f 100644 --- a/src/lib/components/ProgressiveImage.svelte +++ b/src/lib/components/ProgressiveImage.svelte @@ -1,42 +1,42 @@
{ - const db = platform?.env?.DB; +export async function getPhotos( + platform: App.Platform | undefined, +): Promise { + const db = platform?.env?.DB; - if (!db) { - return []; - } + if (!db) { + return []; + } - const result = await db.prepare("SELECT * FROM photos ORDER BY date DESC").all(); + const result = await db + .prepare("SELECT * FROM photos ORDER BY date DESC") + .all(); - const photos: ImageItem[] = result.results.map((row: Record) => ({ - slug: row.slug as string, - title: row.title as string, - date: row.date as string, - image: `${R2_BASE_URL}/${row.image_key}`, - thumb: `${R2_BASE_URL}/${row.thumb_key}`, - type: row.type as string, - camera: row.camera as string, - lens: row.lens as string, - aperture: row.aperture as string, - exposure: row.exposure as string, - focalLength: row.focal_length as string, - iso: row.iso as string, - make: row.make as string, - tags: [], - blurData: row.blur_data as string, - })); + const photos: ImageItem[] = result.results.map( + (row: Record) => ({ + slug: row.slug as string, + title: row.title as string, + date: row.date as string, + image: `${R2_BASE_URL}/${row.image_key}`, + thumb: `${R2_BASE_URL}/${row.thumb_key}`, + type: row.type as string, + camera: row.camera as string, + lens: row.lens as string, + aperture: row.aperture as string, + exposure: row.exposure as string, + focalLength: row.focal_length as string, + iso: row.iso as string, + make: row.make as string, + tags: [], + blurData: row.blur_data as string, + }), + ); - return photos; + return photos; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 7fd9000..2d7fb80 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,19 +1,19 @@ diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 6abd9e5..b66308d 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -7,32 +7,36 @@ const R2_BASE_URL = "https://r2.steve.photo"; const PAGE_SIZE = 15; export const load: PageServerLoad = async ({ platform }) => { - const db = platform?.env?.DB; + const db = platform?.env?.DB; - const result = await db - .prepare("SELECT * FROM photos ORDER BY date DESC LIMIT ?") - .bind(PAGE_SIZE) - .all(); + const result = await db + .prepare("SELECT * FROM photos ORDER BY date DESC LIMIT ?") + .bind(PAGE_SIZE) + .all(); - const countResult = await db.prepare("SELECT COUNT(*) as total FROM photos").first(); - const total = (countResult?.total as number) || 0; + const countResult = await db + .prepare("SELECT COUNT(*) as total FROM photos") + .first(); + const total = (countResult?.total as number) || 0; - const photos: ImageItem[] = result.results.map((row: Record) => ({ - slug: row.slug, - title: row.title, - date: row.date, - image: `${R2_BASE_URL}/${row.image_key}`, - thumb: `${R2_BASE_URL}/${row.thumb_key}`, - type: row.type, - camera: row.camera, - lens: row.lens, - aperture: row.aperture, - exposure: row.exposure, - focalLength: row.focal_length, - iso: row.iso, - make: row.make, - blurData: row.blur_data as string, - })); + const photos: ImageItem[] = result.results.map( + (row: Record) => ({ + slug: row.slug, + title: row.title, + date: row.date, + image: `${R2_BASE_URL}/${row.image_key}`, + thumb: `${R2_BASE_URL}/${row.thumb_key}`, + type: row.type, + camera: row.camera, + lens: row.lens, + aperture: row.aperture, + exposure: row.exposure, + focalLength: row.focal_length, + iso: row.iso, + make: row.make, + blurData: row.blur_data as string, + }), + ); - return { photos, total, pageSize: PAGE_SIZE }; + return { photos, total, pageSize: PAGE_SIZE }; }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 82a0855..2ad2422 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,67 +1,67 @@
diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte index 7b379b6..b7af0a1 100644 --- a/src/routes/admin/+page.svelte +++ b/src/routes/admin/+page.svelte @@ -1,183 +1,185 @@ diff --git a/src/routes/api/photos/+server.ts b/src/routes/api/photos/+server.ts index 3d1b270..f9e9eda 100644 --- a/src/routes/api/photos/+server.ts +++ b/src/routes/api/photos/+server.ts @@ -6,30 +6,32 @@ const R2_BASE_URL = "https://r2.steve.photo"; const PAGE_SIZE = 15; export const GET: RequestHandler = async ({ url, platform }) => { - const db = platform?.env?.DB; - const offset = parseInt(url.searchParams.get("offset") || "0", 10); + const db = platform?.env?.DB; + const offset = parseInt(url.searchParams.get("offset") || "0", 10); - const result = await db - .prepare("SELECT * FROM photos ORDER BY date DESC LIMIT ? OFFSET ?") - .bind(PAGE_SIZE, offset) - .all(); + const result = await db + .prepare("SELECT * FROM photos ORDER BY date DESC LIMIT ? OFFSET ?") + .bind(PAGE_SIZE, offset) + .all(); - const photos: ImageItem[] = result.results.map((row: Record) => ({ - slug: row.slug as string, - title: row.title as string, - date: row.date as string, - image: `${R2_BASE_URL}/${row.image_key}`, - thumb: `${R2_BASE_URL}/${row.thumb_key}`, - type: row.type as string, - camera: row.camera as string, - lens: row.lens as string, - aperture: row.aperture as string, - exposure: row.exposure as string, - focalLength: row.focal_length as string, - iso: row.iso as string, - make: row.make as string, - blurData: row.blur_data as string, - })); + const photos: ImageItem[] = result.results.map( + (row: Record) => ({ + slug: row.slug as string, + title: row.title as string, + date: row.date as string, + image: `${R2_BASE_URL}/${row.image_key}`, + thumb: `${R2_BASE_URL}/${row.thumb_key}`, + type: row.type as string, + camera: row.camera as string, + lens: row.lens as string, + aperture: row.aperture as string, + exposure: row.exposure as string, + focalLength: row.focal_length as string, + iso: row.iso as string, + make: row.make as string, + blurData: row.blur_data as string, + }), + ); - return json({ photos }); + return json({ photos }); }; diff --git a/src/routes/layout.css b/src/routes/layout.css index 60ffed4..1325f01 100644 --- a/src/routes/layout.css +++ b/src/routes/layout.css @@ -1,5 +1,5 @@ -@import 'tailwindcss'; +@import "tailwindcss"; -html { - @apply bg-[#121113] min-h-screen w-full font-mono text-white; +html { + @apply bg-[#121113] min-h-screen w-full font-mono text-white; } diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index 7331708..436e533 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -3,44 +3,44 @@ import { fail, redirect } from "@sveltejs/kit"; import { verifyPassword, createSession } from "$lib"; export const load: PageServerLoad = async ({ locals }) => { - if (locals.user?.authenticated) { - throw redirect(302, "/admin"); - } - return {}; + if (locals.user?.authenticated) { + throw redirect(302, "/admin"); + } + return {}; }; export const actions: Actions = { - default: async ({ request, platform, cookies }) => { - const data = await request.formData(); - const password = data.get("password"); + default: async ({ request, platform, cookies }) => { + const data = await request.formData(); + const password = data.get("password"); - if (!password || typeof password !== "string") { - return fail(400, { error: "Password is required" }); - } + if (!password || typeof password !== "string") { + return fail(400, { error: "Password is required" }); + } - const secret = platform?.env?.SESSION_SECRET; - const passwordHash = platform?.env?.ADMIN_PASSWORD_HASH; + const secret = platform?.env?.SESSION_SECRET; + const passwordHash = platform?.env?.ADMIN_PASSWORD_HASH; - if (!secret || !passwordHash) { - return fail(500, { error: "Server configuration error" }); - } + if (!secret || !passwordHash) { + return fail(500, { error: "Server configuration error" }); + } - const isValid = await verifyPassword(password, passwordHash, secret); + const isValid = await verifyPassword(password, passwordHash, secret); - if (!isValid) { - return fail(401, { error: "Invalid password" }); - } + if (!isValid) { + return fail(401, { error: "Invalid password" }); + } - const session = await createSession(secret); + const session = await createSession(secret); - cookies.set("session", session, { - path: "/", - httpOnly: true, - secure: true, - sameSite: "strict", - maxAge: 60 * 60 * 24, // 24 hours - }); + cookies.set("session", session, { + path: "/", + httpOnly: true, + secure: true, + sameSite: "strict", + maxAge: 60 * 60 * 24, // 24 hours + }); - throw redirect(302, "/admin"); - }, + throw redirect(302, "/admin"); + }, }; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index a02fc7b..e596055 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -1,7 +1,7 @@ diff --git a/src/routes/photo/[slug]/+page.server.ts b/src/routes/photo/[slug]/+page.server.ts index c345f3f..a30665b 100644 --- a/src/routes/photo/[slug]/+page.server.ts +++ b/src/routes/photo/[slug]/+page.server.ts @@ -5,31 +5,34 @@ import { error } from "@sveltejs/kit"; const R2_BASE_URL = "https://r2.steve.photo"; export const load: PageServerLoad = async ({ platform, params }) => { - const db = platform?.env?.DB; + const db = platform?.env?.DB; - const result = await db.prepare("SELECT * FROM photos WHERE slug = ?").bind(params.slug).first(); + const result = await db + .prepare("SELECT * FROM photos WHERE slug = ?") + .bind(params.slug) + .first(); - if (!result) { - throw error(404, "Photo not found"); - } + if (!result) { + throw error(404, "Photo not found"); + } - const photo: ImageItem = { - slug: result.slug as string, - title: result.title as string, - date: result.date as string, - image: `${R2_BASE_URL}/${result.image_key}`, - thumb: `${R2_BASE_URL}/${result.thumb_key}`, - type: result.type as string, - camera: result.camera as string, - lens: result.lens as string, - aperture: result.aperture as string, - exposure: result.exposure as string, - focalLength: result.focal_length as string, - iso: result.iso as string, - make: result.make as string, - tags: [], - blurData: result.blur_data as string, - }; + const photo: ImageItem = { + slug: result.slug as string, + title: result.title as string, + date: result.date as string, + image: `${R2_BASE_URL}/${result.image_key}`, + thumb: `${R2_BASE_URL}/${result.thumb_key}`, + type: result.type as string, + camera: result.camera as string, + lens: result.lens as string, + aperture: result.aperture as string, + exposure: result.exposure as string, + focalLength: result.focal_length as string, + iso: result.iso as string, + make: result.make as string, + tags: [], + blurData: result.blur_data as string, + }; - return { photo }; + return { photo }; }; diff --git a/src/routes/rss.xml/+server.ts b/src/routes/rss.xml/+server.ts index 18183db..9e680f1 100644 --- a/src/routes/rss.xml/+server.ts +++ b/src/routes/rss.xml/+server.ts @@ -3,48 +3,48 @@ import { getPhotos } from "$lib/feed"; import type { RequestHandler } from "./$types"; export const GET: RequestHandler = async ({ platform }) => { - const feed = new Feed({ - title: "steve.photo", - description: "Personal photography portolio of Steve Simkins", - id: "https://steve.photo", - link: "https://steve.photo/", - language: "en", - favicon: "https://steve.photo/favicon.ico", - copyright: `Copyright ${new Date().getFullYear().toString()}, Steve Simkins`, - feedLinks: { - rss: "https://steve.photo/rss.xml", - }, - author: { - name: "Steve Simkins", - email: "contact@stevedylan.dev", - link: "https://stevedylan.dev", - }, - ttl: 60, - }); + const feed = new Feed({ + title: "steve.photo", + description: "Personal photography portolio of Steve Simkins", + id: "https://steve.photo", + link: "https://steve.photo/", + language: "en", + favicon: "https://steve.photo/favicon.ico", + copyright: `Copyright ${new Date().getFullYear().toString()}, Steve Simkins`, + feedLinks: { + rss: "https://steve.photo/rss.xml", + }, + author: { + name: "Steve Simkins", + email: "contact@stevedylan.dev", + link: "https://stevedylan.dev", + }, + ttl: 60, + }); - const photos = await getPhotos(platform); + const photos = await getPhotos(platform); - for (const photo of photos) { - feed.addItem({ - title: photo.title, - id: `https://steve.photo/photo/${photo.slug}`, - link: `https://steve.photo/photo/${photo.slug}`, - date: new Date(photo.date), - image: photo.image, - author: [ - { - name: "Steve Simkins", - email: "contact@stevedylan.dev", - link: "https://stevedylan.dev", - }, - ], - content: `${photo.title}

Camera: ${photo.camera} | Lens: ${photo.lens} | ${photo.aperture} | ${photo.exposure} | ISO ${photo.iso}

`, - }); - } + for (const photo of photos) { + feed.addItem({ + title: photo.title, + id: `https://steve.photo/photo/${photo.slug}`, + link: `https://steve.photo/photo/${photo.slug}`, + date: new Date(photo.date), + image: photo.image, + author: [ + { + name: "Steve Simkins", + email: "contact@stevedylan.dev", + link: "https://stevedylan.dev", + }, + ], + content: `${photo.title}

Camera: ${photo.camera} | Lens: ${photo.lens} | ${photo.aperture} | ${photo.exposure} | ISO ${photo.iso}

`, + }); + } - return new Response(feed.rss2(), { - headers: { - "Content-Type": "application/xml; charset=utf-8", - }, - }); + return new Response(feed.rss2(), { + headers: { + "Content-Type": "application/xml; charset=utf-8", + }, + }); };