diff --git a/eslint.config.js b/eslint.config.js index 9b34ef5..f1748e2 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -20,7 +20,21 @@ export default ts.config( languageOptions: { globals: { ...globals.browser, ...globals.node } }, - rules: { 'no-undef': 'off' } + rules: { + 'no-undef': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + args: 'all', + argsIgnorePattern: '^_', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true + } + ] + } }, { files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], diff --git a/package-lock.json b/package-lock.json index d212252..e719474 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.8", "@tsconfig/svelte": "^5.0.4", + "@types/jsonwebtoken": "^9.0.9", "eslint": "^9.28.0", "eslint-config-prettier": "^10.1.5", "eslint-plugin-svelte": "^3.9.2", @@ -1659,6 +1660,34 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz", + "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz", + "integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -6085,6 +6114,13 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", diff --git a/package.json b/package.json index 333f4f1..105e951 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.8", "@tsconfig/svelte": "^5.0.4", + "@types/jsonwebtoken": "^9.0.9", "eslint": "^9.28.0", "eslint-config-prettier": "^10.1.5", "eslint-plugin-svelte": "^3.9.2", diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 19b1bcc..4f82972 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,15 +1,15 @@ import { decryptToken } from '$lib/auth'; +import type { Handle } from '@sveltejs/kit'; -/** @type {import('@sveltejs/kit').Handle} */ -export async function handle({ event, resolve }) { +export const handle: Handle = ({ event, resolve }) => { const jwt = event.cookies.get('session'); try { if (jwt) { event.locals.user = decryptToken(jwt); return resolve(event); } - } catch (err) { - await event.cookies.delete('session'); + } catch (_) { + event.cookies.delete('session', {path: '/'}); event.locals.user = null; } return resolve(event); diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 19d290a..958bc6e 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -11,7 +11,7 @@ export function createToken(userData) { return jwt.sign(userData, SECRET, { expiresIn: EXPIRES_IN }); } -export function decryptToken(token) { +export function decryptToken(token: string) { return jwt.verify(token, SECRET); } diff --git a/src/lib/config.ts b/src/lib/config.ts index 5c34af3..0775514 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,3 +1,3 @@ import { readFileSync } from 'fs'; -export default JSON.parse(readFileSync('./config.json')); +export default JSON.parse(readFileSync('./config.json').toString()); diff --git a/src/lib/helper/caseNumberOccupied.ts b/src/lib/helper/caseNumberOccupied.ts index 50062e8..0d95f73 100644 --- a/src/lib/helper/caseNumberOccupied.ts +++ b/src/lib/helper/caseNumberOccupied.ts @@ -1,14 +1,9 @@ import { client } from '$lib/minio'; -/** - * Check if caseNumber is used - * @param {string} caseNumber - * @returns {Promise { const prefix = `${caseNumber}/config.json`; - const promise = new Promise((resolve) => { - let stream = client.listObjectsV2('tatort', prefix, false, ''); + const promise: Promise = new Promise((resolve) => { + const stream = client.listObjectsV2('tatort', prefix, false, ''); stream.on('data', () => { stream.destroy(); resolve(true); diff --git a/src/lib/helper/shortenFileSize.ts b/src/lib/helper/shortenFileSize.ts index 1525dfb..0785886 100644 --- a/src/lib/helper/shortenFileSize.ts +++ b/src/lib/helper/shortenFileSize.ts @@ -2,12 +2,7 @@ const KILO = 1024; const MEGA = KILO * KILO; const GIGA = MEGA * KILO; -/** - * Shortens the size in bytes - * @param {number} size - * @returns{string} - */ -export default function shortenFileSize(size) { +export default function shortenFileSize(size: number): string { const giga = Math.floor(size / GIGA); let remainder = size % GIGA; const mega = Math.floor(remainder / MEGA); diff --git a/src/lib/helper/timeElapsed.ts b/src/lib/helper/timeElapsed.ts index b2f8a73..2628ea7 100644 --- a/src/lib/helper/timeElapsed.ts +++ b/src/lib/helper/timeElapsed.ts @@ -4,12 +4,7 @@ const DAY = 24 * HOUR; const YEAR = 365 * DAY; const MONTH = YEAR / 12; -/** - * get readable string of time elapsed since date - * @param {Date} date - * @returns string - */ -export default function timeElapsed(date) { +export default function timeElapsed(date: Date): string { const now = new Date(); const age = Math.floor((now.getTime() - date.getTime()) / 1000); diff --git a/src/routes/(angemeldet)/+layout.server.ts b/src/routes/(angemeldet)/+layout.server.ts index c54cfba..0454232 100644 --- a/src/routes/(angemeldet)/+layout.server.ts +++ b/src/routes/(angemeldet)/+layout.server.ts @@ -1,7 +1,7 @@ -import { redirect } from '@sveltejs/kit'; +import { redirect, type ServerLoadEvent } from '@sveltejs/kit'; +import type { PageServerLoad } from './view/[vorgang]/[tatort]/$types'; -/** @type {import('./$types').PageServerLoad} */ -export function load(event) { +export const load: PageServerLoad = (event: ServerLoadEvent) => { if (!event.locals.user && event.url.pathname !== '/anmeldung') throw redirect(303, '/anmeldung'); return { user: event.locals.user diff --git a/src/routes/(angemeldet)/+layout.svelte b/src/routes/(angemeldet)/+layout.svelte index 77dd926..3894405 100644 --- a/src/routes/(angemeldet)/+layout.svelte +++ b/src/routes/(angemeldet)/+layout.svelte @@ -1,6 +1,5 @@ diff --git a/src/routes/(angemeldet)/+page.svelte b/src/routes/(angemeldet)/+page.svelte index 6148a0b..b0c9d8b 100644 --- a/src/routes/(angemeldet)/+page.svelte +++ b/src/routes/(angemeldet)/+page.svelte @@ -2,7 +2,6 @@ diff --git a/src/routes/(angemeldet)/list/+page.svelte b/src/routes/(angemeldet)/list/+page.svelte index 3f56b0b..2d2f20f 100644 --- a/src/routes/(angemeldet)/list/+page.svelte +++ b/src/routes/(angemeldet)/list/+page.svelte @@ -1,12 +1,5 @@ - - @@ -98,14 +87,19 @@ {item.name} + style="padding: 2px" + id="del__{item.name}" + on:click|preventDefault={delete_item} + aria-label="Vorgang {item.name} löschen" + > + + + + diff --git a/src/routes/(angemeldet)/list/[vorgang]/+page.svelte b/src/routes/(angemeldet)/list/[vorgang]/+page.svelte index 13551c8..1ac200c 100644 --- a/src/routes/(angemeldet)/list/[vorgang]/+page.svelte +++ b/src/routes/(angemeldet)/list/[vorgang]/+page.svelte @@ -1,11 +1,5 @@ - -
@@ -190,93 +181,98 @@
{ - defocus_element(i); - }} - on:keydown|stopPropagation={ - // event needed to identify ID - // TO-DO: check if event is needed or if index is sufficient - async (ev) => {handle_input(ev, i)} - } - - >{item.name} + style="display: inline-block; min-width: 5px;" + id="label__{item.name}" + class="text-sm font-semibold leading-6 text-gray-900" + contenteditable={!item.show_button} + role="textbox" + tabindex="0" + aria-label="Dateiname bearbeiten" + on:focusout={() => { + defocus_element(i); + }} + on:keydown|stopPropagation={// event needed to identify ID + // TO-DO: check if event is needed or if index is sufficient + async (ev) => { + handle_input(ev, i); + }}>{item.name} {#if item.show_button} - + {/if} + style="padding: 2px" + id="del__{item.name}" + on:click|preventDefault={async (ev) => { + let delete_item = window.confirm('Bist du sicher?'); + + if (delete_item) { + // bucket: tatort, name: /item-name + let vorgang = $page.params.vorgang; + let filename = ''; + if (ev && ev.currentTarget && (ev.currentTarget as HTMLElement).id) { + filename = (ev.currentTarget as HTMLElement).id.split('del__')[1]; + } + + // delete request + // -------------- + + let url = new URL($page.url); + url.pathname += `/${filename}`; + + try { + const response = await fetch(url, { method: 'DELETE' }); + if (response.status == 204) { + setTimeout(() => { + window.location.reload(); + }, 500); + } + } catch (error) { + if (error instanceof Error) { + console.log(error.message); + } else { + console.log(error); + } + } + } + }} + aria-label="Datei löschen" + > + +

{shortenFileSize(item.size)}

@@ -296,7 +292,7 @@
-Umbenennen {#if inProgress}

Vorgang läuft...

@@ -307,5 +303,10 @@
-
+ + diff --git a/src/routes/(angemeldet)/list/[vorgang]/+server.ts b/src/routes/(angemeldet)/list/[vorgang]/+server.ts index d090407..98611df 100644 --- a/src/routes/(angemeldet)/list/[vorgang]/+server.ts +++ b/src/routes/(angemeldet)/list/[vorgang]/+server.ts @@ -3,25 +3,24 @@ import { json } from '@sveltejs/kit'; // rename operation -export async function PUT({ request }) { +export async function PUT({ request }: {request: Request}) { const data = await request.json(); - console.log(`--- ${request.url.split('/').at(-1)} +++ ${JSON.stringify(data)}`); // Vorgang - let vorgang = request.url.split('/').at(-1) + const vorgang = request.url.split('/').at(-1); // prepare copy, incl. check if new name exists already - let old_name = data["old_name"] - let src_full_path = `/tatort/${vorgang}/${old_name}` - let new_name = `${vorgang}/${data["new_name"]}` + const old_name = data["old_name"]; + const src_full_path = `/tatort/${vorgang}/${old_name}`; + const new_name = `${vorgang}/${data["new_name"]}`; try { - let file_stats = await client.statObject('tatort', new_name) - return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 }) + await client.statObject('tatort', new_name); + return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 }); } catch (error) { // continue operation - console.log('continue operation') + console.log(error, 'continue operation'); } // actual copy operation diff --git a/src/routes/(angemeldet)/list/[vorgang]/[tatort]/+server.ts b/src/routes/(angemeldet)/list/[vorgang]/[tatort]/+server.ts index 76dd05d..9861268 100644 --- a/src/routes/(angemeldet)/list/[vorgang]/[tatort]/+server.ts +++ b/src/routes/(angemeldet)/list/[vorgang]/[tatort]/+server.ts @@ -1,13 +1,11 @@ import { client } from '$lib/minio'; -import { json } from '@sveltejs/kit'; -export async function DELETE({ request }) { +export async function DELETE({ request }: { request: Request }) { + const url_fragments = request.url.split('/'); + const item = url_fragments.at(-1); + const vorgang = url_fragments.at(-2); - let url_fragments = request.url.split('/') - let item = url_fragments.at(-1); - let vorgang = url_fragments.at(-2); + await client.removeObject('tatort', `${vorgang}/${item}`); - await client.removeObject('tatort', `${vorgang}/${item}`) - - return new Response(null, { status: 204 }); -}; + return new Response(null, { status: 204 }); +} diff --git a/src/routes/(angemeldet)/tatorte/+page.server.ts b/src/routes/(angemeldet)/tatorte/+page.server.ts index 48f197d..5004d69 100644 --- a/src/routes/(angemeldet)/tatorte/+page.server.ts +++ b/src/routes/(angemeldet)/tatorte/+page.server.ts @@ -5,7 +5,7 @@ import caseNumberOccupied from '$lib/helper/caseNumberOccupied'; /** @type {import('./$types').Actions} */ export const actions = { - default: async ({ request }) => { + default: async ({ request }: {request: Request}) => { const data = await request.formData(); const caseNumber = data.get('caseNumber'); const description = data.get('description'); @@ -28,7 +28,7 @@ export const actions = { const config = `${JSON.stringify({ caseNumber, description, version: 1 })}\n`; - await client.putObject('tatort', `${caseNumber}/config.json`, config, { + await client.putObject('tatort', `${caseNumber}/config.json`, config, undefined, { 'Content-Type': 'application/json' }); diff --git a/src/routes/(angemeldet)/tatorte/+page.svelte b/src/routes/(angemeldet)/tatorte/+page.svelte index 2920917..0564923 100644 --- a/src/routes/(angemeldet)/tatorte/+page.svelte +++ b/src/routes/(angemeldet)/tatorte/+page.svelte @@ -55,22 +55,22 @@
- {#if form?.error?.description} -

{form.error.description}

+ {#if form?.error} +

{form.description}

{/if}
diff --git a/src/routes/(angemeldet)/upload/+page.server.ts b/src/routes/(angemeldet)/upload/+page.server.ts index b63aa7e..27cc6c9 100644 --- a/src/routes/(angemeldet)/upload/+page.server.ts +++ b/src/routes/(angemeldet)/upload/+page.server.ts @@ -1,13 +1,8 @@ -import path from 'path'; -import { writeFile } from 'fs/promises'; -import { createReadStream } from 'fs'; -/** import Minio from 'minio'; */ import { Readable } from 'stream'; -/** const MINIO_ACCESS_KEY = 'tMhLrfog47lMm0HZ'; */ import { client } from '$lib/minio'; import { fail } from '@sveltejs/kit'; -function isRequiredFieldValid(value) { +const isRequiredFieldValid = (value: unknown) => { if (value == null) return false; if (typeof value === 'string' || value instanceof String) return value.trim() !== ''; @@ -15,9 +10,8 @@ function isRequiredFieldValid(value) { return true; } -/** @type {import('./$types').Actions} */ export const actions = { - url: async ({ request }) => { + url: async ({ request }: {request: Request}) => { const data = await request.formData(); const vorgang = data.get('vorgang'); const name = data.get('name'); @@ -30,20 +24,20 @@ export const actions = { if (!objectName.endsWith('.png')) objectName += '.png'; break; case '': - if (fileName.endsWith('.glb') && !objectName.endsWith('.glb')) objectName += '.glb'; + if (fileName?.toString().endsWith('.glb') && !objectName.endsWith('.glb')) objectName += '.glb'; } const url = await client.presignedPutObject('tatort', objectName); return { url }; }, - validate: async ({ request }) => { + validate: async ({ request }: {request: Request}) => { const requestData = await request.formData(); const data = Object.fromEntries(requestData); const vorgang = data.vorgang; const name = data.name; let success = true; - let err = {}; + const err = {}; if (isRequiredFieldValid(vorgang)) err.vorgang = null; else { @@ -62,24 +56,21 @@ export const actions = { return fail(400, err); }, - upload: async ({ request }) => { + upload: async ({ request }: {request: Request}) => { const requestData = await request.formData(); const data = Object.fromEntries(requestData); const vorgang = data.vorgang; const name = data.name; - console.log('I:', vorgang, name); const url = await client.presignedPutObject('tatort', `${vorgang}/${name}`, 60); - console.log('O:', url); return { url }; }, - upload3: async ({ request }) => { + upload3: async ({ request }: {request: Request}) => { const requestData = await request.formData(); const data = Object.fromEntries(requestData); const name = data.name; const stream = data.file.stream(); - console.log('Data:', stream); const metaData = { 'Content-Type': 'model-gtlf-binary', 'X-VorgangsNr': '4711' }; const result = new Promise((resolve, reject) => { client.putObject('tatort', name, Readable.from(stream), metaData, function (err, etag) { @@ -91,7 +82,6 @@ export const actions = { let error = null; try { etag = await result; - console.log(etag); } catch (err) { error = err; console.log('Error:', err); diff --git a/src/routes/(angemeldet)/upload/+page.svelte b/src/routes/(angemeldet)/upload/+page.svelte index ae8c820..5f7b5c2 100644 --- a/src/routes/(angemeldet)/upload/+page.svelte +++ b/src/routes/(angemeldet)/upload/+page.svelte @@ -1,5 +1,5 @@