9 Commits

9 changed files with 122 additions and 38 deletions

View File

@@ -51,6 +51,7 @@
"minio": "^8.0.5", "minio": "^8.0.5",
"postcss": "^8.5.4", "postcss": "^8.5.4",
"sqlite3": "^5.1.7", "sqlite3": "^5.1.7",
"tailwindcss": "^3.4.17" "tailwindcss": "^3.4.17",
"uuid": "^11.1.0"
} }
} }

View File

@@ -19,21 +19,27 @@ let check_ins_stmt = `INSERT INTO users (name, pw) SELECT 'admin', '${hashed_pw}
db.exec(check_ins_stmt); db.exec(check_ins_stmt);
let users_stmt = `SELECT * FROM USERS`; let users_stmt = `SELECT * FROM USERS`;
const stmt = db.prepare(users_stmt); let stmt = db.prepare(users_stmt);
console.log(`\n`, `*** Users table`);
for (const usr of stmt.iterate()) { for (const usr of stmt.iterate()) {
console.log(`xxx ${usr.name} + ${usr.pw}`) console.log(`[r] ${usr.name} + ${usr.pw}`);
}; }
// cases table // cases table
create_stmt = `CREATE TABLE IF NOT EXISTS cases create_stmt = `CREATE TABLE IF NOT EXISTS cases
(id INTEGER PRIMARY KEY AUTOINCREMENT, (id INTEGER PRIMARY KEY AUTOINCREMENT,
token TEXT NOT NULL UNIQUE, token TEXT NOT NULL UNIQUE,
name TEXT NOT NULL, name TEXT NOT NULL,
pw TEXT NOT NULL, pw TEXT NOT NULL)`;
created_by INTEGER NOT NULL,
FOREIGN KEY(created_by) REFERENCES users(id))`;
db.exec(create_stmt); db.exec(create_stmt);
let cases_stmt = `SELECT * FROM cases`;
stmt = db.prepare(cases_stmt);
console.log(`\n`, `*** Cases table`);
for (const usr of stmt.iterate()) {
console.log(`[r] ${usr.name} + ${usr.token} + ${usr.pw}`);
}
db.close(); db.close();

View File

@@ -2,6 +2,9 @@ import { fail } from '@sveltejs/kit';
import { BUCKET, client, CONFIGFILENAME, TOKENFILENAME } from '$lib/minio'; import { BUCKET, client, CONFIGFILENAME, TOKENFILENAME } from '$lib/minio';
import { checkIfExactDirectoryExists, getContentOfTextObject } from './s3ClientService'; import { checkIfExactDirectoryExists, getContentOfTextObject } from './s3ClientService';
import Database from 'better-sqlite3';
const db = new Database('./src/lib/data/tatort.db');
/** /**
* Get Vorgang and corresponend list of tatorte * Get Vorgang and corresponend list of tatorte
* @param caseId * @param caseId
@@ -17,13 +20,25 @@ export const getVorgangByCaseId = async (caseId: string) => {
const splittedNameParts = chunk.name.split('/'); const splittedNameParts = chunk.name.split('/');
const prefix = splittedNameParts[0]; const prefix = splittedNameParts[0];
const name = splittedNameParts[1]; const name = splittedNameParts[1];
if (name === CONFIGFILENAME || name === TOKENFILENAME) continue; if (name === CONFIGFILENAME || name === TOKENFILENAME) continue;
list.push({ ...chunk, name: name, prefix: prefix, show_button: true }); list.push({ ...chunk, name: name, prefix: prefix, show_button: true });
} }
return list; return list;
}; };
/**
* Get Vorgang
* @param caseId
* @returns caseObj with keys `token`, `name`, `pw` || undefined
*/
export const getVorgang = function (caseId: string) {
let getVorgang_stmt = `SELECT token, name, pw FROM cases WHERE token = ?`;
const stmt = db.prepare(getVorgang_stmt);
const res = stmt.get(caseId);
return res;
};
/** /**
* Fetches list of vorgänge from s3 bucket * Fetches list of vorgänge from s3 bucket
@@ -31,7 +46,7 @@ export const getVorgangByCaseId = async (caseId: string) => {
*/ */
export const getListOfVorgänge = async () => { export const getListOfVorgänge = async () => {
const stream = client.listObjectsV2(BUCKET, '', false, ''); const stream = client.listObjectsV2(BUCKET, '', false, '');
const list = []; const list = [];
for await (const chunk of stream) { for await (const chunk of stream) {
const objPath = `${chunk.prefix}${TOKENFILENAME}`; const objPath = `${chunk.prefix}${TOKENFILENAME}`;
@@ -44,6 +59,23 @@ export const getListOfVorgänge = async () => {
return list; return list;
}; };
/**
* Fetches list of vorgänge from database
* @returns list with of available cases
*/
export const getVorgaenge = function () {
let getVorgaenge_stmt = `SELECT token, name, pw from cases`;
const stmt = db.prepare(getVorgaenge_stmt);
const res = stmt.all();
const vorgaenge_list = [];
for (const r of res) {
const vorg = { token: r.token, name: r.name, pw: r.pw };
vorgaenge_list.push(vorg);
}
return vorgaenge_list;
};
/** /**
* Checks if Vorgang exists * Checks if Vorgang exists
* @param request * @param request
@@ -69,6 +101,23 @@ export const checkIfVorgangExists = async (caseId: string | null) => {
return true; return true;
}; };
export const vorgangExists = function (caseId: string | null) {
if (!caseId) {
return fail(400, {
success: false,
caseId,
error: { message: 'Die Vorgangsnummer darf nicht leer sein.' }
});
}
let vorgaenge = getVorgaenge();
const vorgaenge_tokens = vorgaenge.map((vorg) => vorg.token);
const found = vorgaenge_tokens.indexOf(caseId) != -1;
return found;
};
export const hasValidToken = async (caseId: string, caseToken: string) => { export const hasValidToken = async (caseId: string, caseToken: string) => {
const objPath = `${caseId}/${TOKENFILENAME}`; const objPath = `${caseId}/${TOKENFILENAME}`;
@@ -90,3 +139,17 @@ export const hasValidToken = async (caseId: string, caseToken: string) => {
} }
} }
}; };
export const tokenValid = function (caseId, caseToken) {
if (!caseToken) {
return false;
}
const vorg = getVorgang(caseId);
if (!vorg || vorg.pw !== caseToken) {
return false;
}
return true;
};

View File

@@ -1,10 +1,11 @@
import { getListOfVorgänge } from '$lib/server/vorgangService'; import { getListOfVorgänge, getVorgaenge } from '$lib/server/vorgangService';
import type { PageServerLoad } from '../../(token-based)/view/$types'; import type { PageServerLoad } from '../../(token-based)/view/$types';
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async () => {
const caseList = await getListOfVorgänge(); // const caseList = await getListOfVorgänge();
const caseList = getVorgaenge();
return { return {
caseList caseList
}; };
}; };

View File

@@ -46,7 +46,7 @@
<ul role="list" class="divide-y divide-gray-100"> <ul role="list" class="divide-y divide-gray-100">
{#each caseList as item} {#each caseList as item}
<li> <li>
<a href="/list/{item.name}?token={item.token}" class="flex justify-between gap-x-6 py-5"> <a href="/list/{item.token}?token={item.pw}" class="flex justify-between gap-x-6 py-5">
<div class="flex gap-x-4"> <div class="flex gap-x-4">
<!-- Ordner --> <!-- Ordner -->
<Folder /> <Folder />

View File

@@ -2,6 +2,9 @@ import { Buffer } from 'buffer';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { client } from '$lib/minio'; import { client } from '$lib/minio';
import { fail } from '@sveltejs/kit'; import { fail } from '@sveltejs/kit';
import { v4 as uuidv4 } from 'uuid';
import Database from 'better-sqlite3';
const isRequiredFieldValid = (value: unknown) => { const isRequiredFieldValid = (value: unknown) => {
if (value == null) return false; if (value == null) return false;
@@ -9,10 +12,10 @@ const isRequiredFieldValid = (value: unknown) => {
if (typeof value === 'string' || value instanceof String) return value.trim() !== ''; if (typeof value === 'string' || value instanceof String) return value.trim() !== '';
return true; return true;
} };
export const actions = { export const actions = {
url: async ({ request }: {request: Request}) => { url: async ({ request }: { request: Request }) => {
const data = await request.formData(); const data = await request.formData();
const vorgang = data.get('vorgang'); const vorgang = data.get('vorgang');
const name = data.get('name'); const name = data.get('name');
@@ -20,28 +23,29 @@ export const actions = {
const code = data.get('zugangscode'); const code = data.get('zugangscode');
const fileName = data.get('fileName'); const fileName = data.get('fileName');
let objectName = `${vorgang}/${name}`; // store case in database
let db = new Database('./src/lib/data/tatort.db');
let token = uuidv4();
let insert_stmt = `INSERT INTO cases (token, name, pw) VALUES (?, ?, ?)`;
const stmt = db.prepare(insert_stmt);
stmt.run(token, vorgang, code);
let objectName = `${token}/${name}`;
switch (type) { switch (type) {
case 'image/png': case 'image/png':
if (!objectName.endsWith('.png')) objectName += '.png'; if (!objectName.endsWith('.png')) objectName += '.png';
break; break;
case '': case '':
if (fileName?.toString().endsWith('.glb') && !objectName.endsWith('.glb')) objectName += '.glb'; if (fileName?.toString().endsWith('.glb') && !objectName.endsWith('.glb'))
objectName += '.glb';
} }
const url = await client.presignedPutObject('tatort', objectName); const url = await client.presignedPutObject('tatort', objectName);
// store code in S3
// tatort/<vorgang>/__perm__
const code_filename = '__perm__';
const buf = Buffer.from(code, 'utf-8');
const code_stream = Readable.from(buf);
const code_path = `${vorgang}/${code_filename}`;
await client.putObject('tatort', code_path, code_stream);
return { url }; return { url };
}, },
validate: async ({ request }: {request: Request}) => { validate: async ({ request }: { request: Request }) => {
const requestData = await request.formData(); const requestData = await request.formData();
const data = Object.fromEntries(requestData); const data = Object.fromEntries(requestData);
const vorgang = data.vorgang; const vorgang = data.vorgang;
@@ -73,7 +77,7 @@ export const actions = {
return fail(400, err); return fail(400, err);
}, },
upload: async ({ request }: {request: Request}) => { upload: async ({ request }: { request: Request }) => {
const requestData = await request.formData(); const requestData = await request.formData();
const data = Object.fromEntries(requestData); const data = Object.fromEntries(requestData);
const vorgang = data.vorgang; const vorgang = data.vorgang;
@@ -83,7 +87,7 @@ export const actions = {
return { url }; return { url };
}, },
upload3: async ({ request }: {request: Request}) => { upload3: async ({ request }: { request: Request }) => {
const requestData = await request.formData(); const requestData = await request.formData();
const data = Object.fromEntries(requestData); const data = Object.fromEntries(requestData);
const name = data.name; const name = data.name;

View File

@@ -1,4 +1,9 @@
import { checkIfVorgangExists, hasValidToken } from '$lib/server/vorgangService'; import {
checkIfVorgangExists,
hasValidToken,
tokenValid,
vorgangExists
} from '$lib/server/vorgangService';
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './list/[vorgang]/$types'; import type { PageServerLoad } from './list/[vorgang]/$types';
@@ -12,8 +17,9 @@ export const load: PageServerLoad = async ({ params, url, locals }) => {
const caseId = params.vorgang; const caseId = params.vorgang;
const caseToken = url.searchParams.get('token'); const caseToken = url.searchParams.get('token');
const isVorgangValid = await checkIfVorgangExists(caseId); const isVorgangValid = vorgangExists(caseId);
const isTokenValid = await hasValidToken(caseId, caseToken); const isTokenValid = tokenValid(caseId, caseToken);
console.log(`--- is valid: ${isTokenValid}`);
if (!isVorgangValid || !isTokenValid) throw redirect(303, `/anmeldung`); if (!isVorgangValid || !isTokenValid) throw redirect(303, `/anmeldung`);
}; };

View File

@@ -1,4 +1,4 @@
import { getVorgangByCaseId } from '$lib/server/vorgangService'; import { getVorgang, getVorgangByCaseId } from '$lib/server/vorgangService';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, url }) => { export const load: PageServerLoad = async ({ params, url }) => {
@@ -6,9 +6,11 @@ export const load: PageServerLoad = async ({ params, url }) => {
const caseToken = url.searchParams.get('token'); const caseToken = url.searchParams.get('token');
const crimesList = await getVorgangByCaseId(caseId); const crimesList = await getVorgangByCaseId(caseId);
const vorg = getVorgang(caseId);
return { return {
crimesList, crimesList,
caseToken caseToken,
vorg
}; };
}; };

View File

@@ -26,6 +26,7 @@
// add other properties as needed // add other properties as needed
} }
const vorg = data.vorg;
const crimesList: ListItem[] = data.crimesList; const crimesList: ListItem[] = data.crimesList;
const token: string = data.caseToken; const token: string = data.caseToken;
@@ -132,7 +133,7 @@
<div class="-z-10 bg-white"> <div class="-z-10 bg-white">
<div class="flex flex-col items-center justify-center w-full"> <div class="flex flex-col items-center justify-center w-full">
<h1 class="text-xl">Vorgang {$page.params.vorgang}</h1> <h1 class="text-xl">Vorgang {vorg.name}</h1>
</div> </div>
<div class="mx-auto flex justify-center max-w-7xl h-full"> <div class="mx-auto flex justify-center max-w-7xl h-full">
<ul class="divide-y divide-gray-100"> <ul class="divide-y divide-gray-100">