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: `
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: `
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",
+ },
+ });
};