route: The image component
This commit is contained in:
parent
3ebeb8ed6a
commit
c3e6f481bb
6 changed files with 170 additions and 15 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -12,3 +12,4 @@ node_modules
|
|||
|
||||
dev.db
|
||||
local.db
|
||||
uploads
|
||||
|
|
|
|||
71
src/components/admin/PhotoPostForm.svelte
Normal file
71
src/components/admin/PhotoPostForm.svelte
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<script lang="ts">
|
||||
let canvasElement: HTMLCanvasElement;
|
||||
let errorMessage = $state("");
|
||||
let photoObjectUrl = $state("");
|
||||
|
||||
function handleFileChange(event: Event) {
|
||||
const file = (event.target as HTMLInputElement).files?.[0];
|
||||
|
||||
if (!file) {
|
||||
errorMessage = `No file selected, try again`;
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = canvasElement.getContext("2d");
|
||||
|
||||
if (!ctx) {
|
||||
errorMessage = `Could not get 2d context from canvas`;
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, canvasElement.width, canvasElement.height);
|
||||
|
||||
photoObjectUrl = URL.createObjectURL(file);
|
||||
const image = new Image();
|
||||
image.src = photoObjectUrl;
|
||||
image.onload = () => {
|
||||
const orientation = image.width > image.height ? "landscape" : "portrait";
|
||||
|
||||
if (orientation === "landscape") {
|
||||
ctx.drawImage(image, 0, 0, canvasElement.width, canvasElement.height);
|
||||
} else {
|
||||
ctx.drawImage(image, 0, 0, canvasElement.height, canvasElement.width);
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="error-message">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
<form
|
||||
method="POST"
|
||||
action="/admin/photos"
|
||||
enctype="multipart/form-data"
|
||||
class="admin-form"
|
||||
>
|
||||
<canvas bind:this={canvasElement}></canvas>
|
||||
|
||||
<div class="field">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" name="title" id="title" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<input
|
||||
onchange={handleFileChange}
|
||||
type="file"
|
||||
name="file"
|
||||
accept="image/*"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="description">Description</label>
|
||||
<textarea name="description" id="description"></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit">Upload</button>
|
||||
</form>
|
||||
|
|
@ -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: "/" });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
src/routes/image/[filename]/+server.ts
Normal file
25
src/routes/image/[filename]/+server.ts
Normal file
|
|
@ -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 } });
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_",
|
||||
|
|
|
|||
Loading…
Reference in a new issue