diff --git a/.gitignore b/.gitignore index 10df2f4..2a1d228 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ node_modules dev.db local.db +uploads diff --git a/src/components/admin/PhotoPostForm.svelte b/src/components/admin/PhotoPostForm.svelte new file mode 100644 index 0000000..b83f2b3 --- /dev/null +++ b/src/components/admin/PhotoPostForm.svelte @@ -0,0 +1,71 @@ + + +{#if errorMessage} +
+ {errorMessage} +
+{/if} +
+ + +
+ + +
+ +
+ +
+ +
+ + +
+ + +
diff --git a/src/lib/blog/auth/CookieAuthentication.ts b/src/lib/blog/auth/CookieAuthentication.ts index ff477f7..76d6d3f 100644 --- a/src/lib/blog/auth/CookieAuthentication.ts +++ b/src/lib/blog/auth/CookieAuthentication.ts @@ -1,38 +1,45 @@ import type { Cookies } from "@sveltejs/kit"; export class CookieAuthentication { - private readonly cookieValue: string; private readonly cookieValueArray: string[]; - public static cookieName = 'auth' - public static adminAuthRole = 'admin' + public static cookieName = "auth"; + public static adminAuthRole = "admin"; - constructor( - private readonly cookies: Cookies, - ){ - this.cookieValue = this.cookies.get(CookieAuthentication.cookieName) ?? '' - this.cookieValueArray = this.cookieValue.split(',') + constructor(private readonly cookies: Cookies) { + this.cookieValue = this.cookies.get(CookieAuthentication.cookieName) ?? ""; + this.cookieValueArray = this.cookieValue.split(","); } public get isAuthdAsAdmin(): boolean { let isAuthdAsAdmin = false; if (this.cookieValueArray.includes(CookieAuthentication.adminAuthRole)) { - isAuthdAsAdmin = true + isAuthdAsAdmin = true; } - return isAuthdAsAdmin + return isAuthdAsAdmin; + } + + public logout() { + if (!this.isAuthdAsAdmin) return; + + this.cookies.delete(CookieAuthentication.cookieName, { path: "/" }); } public setAdminAuthentication(isAuthd: boolean) { - let value = this.cookieValue + let value = this.cookieValue; if (isAuthd) { - value = Array.from(new Set([...this.cookieValueArray, CookieAuthentication.adminAuthRole])).join(',') + value = Array.from( + new Set([...this.cookieValueArray, CookieAuthentication.adminAuthRole]), + ).join(","); } else { - value = this.cookieValueArray.filter((i) => i !== CookieAuthentication.adminAuthRole).join(',') + value = this.cookieValueArray + .filter((i) => i !== CookieAuthentication.adminAuthRole) + .join(","); } - this.cookies.set(CookieAuthentication.cookieName, value, { path: '/'}) + this.cookies.set(CookieAuthentication.cookieName, value, { path: "/" }); } } diff --git a/src/routes/image/[filename]/+server.ts b/src/routes/image/[filename]/+server.ts new file mode 100644 index 0000000..e2a2e3e --- /dev/null +++ b/src/routes/image/[filename]/+server.ts @@ -0,0 +1,25 @@ +import { access, readFile } from "node:fs/promises"; +import { constants } from "node:fs"; +import { PRIVATE_PHOTO_UPLOAD_DIR } from "$env/static/private"; +import * as path from "node:path"; +import { error, type ServerLoad } from "@sveltejs/kit"; + +export const GET: ServerLoad = async ({ params, locals }) => { + const { filename } = params; + + const proposedFilePath = path.join(PRIVATE_PHOTO_UPLOAD_DIR, filename); + + const fileExists = await access(proposedFilePath, constants.F_OK) + .then(() => true) + .catch(() => false); + + if (!fileExists) { + return error(404, "File not found"); + } + + const file = await readFile(proposedFilePath); + const fileExt = path.extname(filename); + const contentType = `image/${fileExt.replace(".", "")}`; + + return new Response(file, { headers: { "Content-Type": contentType } }); +}; diff --git a/src/styles/thomaswilson.css b/src/styles/thomaswilson.css index a05b999..bfc3637 100644 --- a/src/styles/thomaswilson.css +++ b/src/styles/thomaswilson.css @@ -213,3 +213,50 @@ a.no-icon::after { content: ""; display: none; } + +/* An Alert-like component*/ +.alert { + --colour-scheme-border: var(--gray-800); + --font-size: var(--font-size-base); + --colour-scheme-text: var(--gray-800); + --colour-scheme-bg: var(--gray-100); + padding: 8px; + border: 1px solid var(--colour-scheme-border); + border-radius: 4px; + font-size: var(--font-size); + letter-spacing: 0px; + color: var(--colour-scheme-text); + background-color: var(--colour-scheme-bg); +} + +.alert.error { + --colour-scheme-border: var(--red-800); + --colour-scheme-text: var(--red-800); + --colour-scheme-bg: var(--red-100); +} + +.admin-form { + display: flex; + flex-direction: column; + gap: 16px; +} + +.admin-form .field { + display: flex; + flex-direction: column; + gap: 4px; +} +.admin-form .field label { + font-size: var(--font-size); + letter-spacing: 0px; + color: var(--colour-scheme-text); +} +.admin-form .field input { + padding: 8px; + border: 1px solid var(--colour-scheme-border); + border-radius: 4px; + font-size: var(--font-size); + letter-spacing: 0px; + color: var(--colour-scheme-text); + background-color: var(--colour-scheme-bg); +} diff --git a/svelte.config.js b/svelte.config.js index 2df868e..475e277 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -10,7 +10,11 @@ const config = { kit: { adapter: adapter({ split: false }), - + alias: { + $lib: "/src/lib", + $srcPrisma: "/src/prisma", + $generatedPrisma: "/generated/prisma/*", + }, env: { publicPrefix: "PUBLIC_", privatePrefix: "PRIVATE_",