import datetime
import logging
import re
-from fastapi import APIRouter, Cookie
+from fastapi import APIRouter, Cookie, Depends, Form, UploadFile, File, HTTPException
from pydantic import BaseModel
-from fastapi import Depends
from . import users
from ..utils import fileHandling
from ..utils import security
import html
+from typing import Annotated
+import time
logger = logging.getLogger("dailytxtLogger")
days.sort(key=lambda x: x["day"])
- return days
\ No newline at end of file
+ return days
+
+'''
+Data ist sent as FormData, not as JSON
+'''
+@router.post("/uploadFile")
+async def uploadFile(day: Annotated[int, Form()], month: Annotated[int, Form()], year: Annotated[int, Form()], uuid: Annotated[str, Form()], file: Annotated[UploadFile, File()], cookie = Depends(users.isLoggedIn)):
+
+ # encrypt file
+ enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
+ encrypted_file = security.encrypt_file(file.file.read(), enc_key)
+ if not fileHandling.writeFile(encrypted_file, cookie["user_id"], uuid):
+ return {"success": False}
+
+ # save file in log
+ content:dict = fileHandling.getDay(cookie["user_id"], year, month)
+ if "days" not in content.keys():
+ content["days"] = []
+ content["days"].append({"day": day, "files": [uuid]})
+###########
+
+
+ print(file.size)
+ print(file.filename)
+ print(uuid)
+ print(file.headers.get("content-type"))
+ print(day, month, year)
+
+ # wait 3 s
+ time.sleep(3)
+ return {"success": True}
\ No newline at end of file
def get_months(user_id, year):
for entry in os.scandir(os.path.join(settings.data_path, str(user_id), year)):
if entry.is_file() and entry.name.endswith(".json"):
- yield entry.name.split(".")[0]
\ No newline at end of file
+ yield entry.name.split(".")[0]
+
+def writeFile(str, user_id, uuid):
+ try:
+ os.makedirs(os.path.join(settings.data_path, str(user_id), 'files'), exist_ok=True)
+ f = open(os.path.join(settings.data_path, str(user_id), 'files', uuid), "w")
+ except Exception as e:
+ logger.exception(e)
+ return False
+ else:
+ with f:
+ f.write(str)
+ return True
+
+def removeFile(user_id, uuid):
+ try:
+ os.remove(os.path.join(settings.data_path, str(user_id), 'files', uuid))
+ except Exception as e:
+ logger.exception(e)
+ return False
+ else:
+ return True
\ No newline at end of file
def decrypt_text(text: str, key: str) -> str:
f = Fernet(key)
- return f.decrypt(text.encode()).decode()
\ No newline at end of file
+ return f.decrypt(text.encode()).decode()
+
+def encrypt_file(file: bytes, key: str) -> str:
+ f = Fernet(key)
+ return f.encrypt(file).decode()
\ No newline at end of file
"marked": "^15.0.6",
"svelte-outside": "^0.0.3",
"tiny-markdown-editor": "^0.1.31",
- "trianglify": "^4.1.1"
+ "trianglify": "^4.1.1",
+ "uuid": "^11.0.5"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
+ "node_modules/uuid": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
+ "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
"node_modules/vite": {
"version": "5.4.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
"marked": "^15.0.6",
"svelte-outside": "^0.0.3",
"tiny-markdown-editor": "^0.1.31",
- "trianglify": "^4.1.1"
+ "trianglify": "^4.1.1",
+ "uuid": "^11.0.5"
}
}
.datepicker {
display: inline-block;
font-family: Arial, sans-serif;
- border: 1px solid #ccc;
+ border: 1px solid #ececec77;
border-radius: 8px;
/* overflow: hidden; */
/* width: 300px; */
box-sizing: border-box;
- backdrop-filter: blur(5px) saturate(150%);
- background-color: rgba(182, 183, 185, 0.75);
+ backdrop-filter: blur(8px) saturate(150%);
+ background-color: rgba(219, 219, 219, 0.45);
}
.datepicker-header {
display: flex;
border-top-right-radius: 0;
overflow-y: auto;
min-height: 250px;
+ backdrop-filter: blur(8px) saturate(150%);
+ background-color: rgba(219, 219, 219, 0.45);
+ border: 1px solid #ececec77;
}
.input-group {
export const readingMode = writable(false);
export const useTrianglify = writable(true);
-export const trianglifyOpacity = writable(0.8);
+export const trianglifyOpacity = writable(0.4);
export const trianglifyColor = writable('');
export const backgroundColor = writable('');
\ No newline at end of file
oldCanvas.remove();
}
+ //xColors: ['#F3F3F3', '#FEFEFE', '#E5E5E5'],
const canvas = trianglify({
width: window.innerWidth,
- height: window.innerHeight
+ height: window.innerHeight,
+ xColors: ['#FA2'],
+ fill: false,
+ strokeWidth: 1,
+ cellSize: 100
});
document.body.appendChild(canvas.toCanvas());
document.querySelector('canvas').style =
- 'position: fixed; top: 0; left: 0; z-index: -1; opacity: 0.8; width: 100%; height: 100%;';
+ 'position: fixed; top: 0; left: 0; z-index: -1; opacity: 0.4; width: 100%; height: 100%; background-color: #eaeaea;';
}
}
import '../../../node_modules/tiny-markdown-editor/dist/tiny-mde.css';
import { API_URL } from '$lib/APIurl.js';
import DatepickerLogic from '$lib/DatepickerLogic.svelte';
+ import { faCloudArrowUp } from '@fortawesome/free-solid-svg-icons';
+ import Fa from 'svelte-fa';
+ import { v4 as uuidv4 } from 'uuid';
axios.interceptors.request.use((config) => {
config.withCredentials = true;
toast.show();
});
}
+
+ function triggerFileInput() {
+ document.getElementById('fileInput').click();
+ }
+
+ function onFileChange(event) {
+ for (let i = 0; i < event.target.files.length; i++) {
+ uploadFile(event.target.files[i]);
+ }
+ }
+
+ let uploadingFiles = $state([]);
+
+ function uploadFile(f) {
+ let uuid = uuidv4();
+
+ uploadingFiles = [...uploadingFiles, { name: f.name, progress: 0, size: f.size, uuid: uuid }];
+
+ const config = {
+ onUploadProgress: (progressEvent) => {
+ uploadingFiles = uploadingFiles.map((file) => {
+ if (file.uuid === uuid) {
+ file.progress = Math.round(progressEvent.progress * 100);
+ }
+ return file;
+ });
+ }
+ };
+
+ const formData = new FormData();
+ formData.append('day', $selectedDate.getDate());
+ formData.append('month', $selectedDate.getMonth() + 1);
+ formData.append('year', $selectedDate.getFullYear());
+ formData.append('file', f);
+ formData.append('uuid', uuid);
+
+ axios
+ .post(API_URL + '/logs/uploadFile', formData, {
+ ...config
+ })
+ .then((response) => {
+ console.log(response);
+ })
+ .catch((error) => {
+ console.error(error);
+ })
+ .finally(() => {
+ uploadingFiles = uploadingFiles.filter((file) => file.uuid !== uuid);
+ });
+ }
</script>
<DatepickerLogic />
</div>
</div>
- <div id="right">Right</div>
+ <div id="right" class="d-flex flex-column">
+ <div>Tags</div>
+
+ <div class="files">
+ <button class="btn btn-secondary" id="uploadBtn" onclick={triggerFileInput}
+ ><Fa icon={faCloudArrowUp} class="me-2" id="uploadIcon" />Upload</button
+ >
+ <input type="file" id="fileInput" multiple style="display: none;" onchange={onFileChange} />
+
+ {#each uploadingFiles as file}
+ <div>
+ {file.name}
+ <div
+ class="progress"
+ role="progressbar"
+ aria-label="Upload progress"
+ aria-valuemin="0"
+ aria-valuemax="100"
+ >
+ <div
+ class="progress-bar {file.progress === 100
+ ? 'progress-bar-striped progress-bar-animated'
+ : ''}"
+ style:width={file.progress + '%'}
+ >
+ {#if file.progress !== 100}
+ {file.progress}%
+ {:else}
+ Wird verschlüsselt...
+ {/if}
+ </div>
+ </div>
+ </div>
+ {/each}
+ </div>
+ </div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div
</div>
<style>
+ :global(#uploadIcon) {
+ transition: all ease 0.3s;
+ }
+
+ :global(#uploadBtn:hover > #uploadIcon) {
+ transform: scale(1.2);
+ }
+
:global(.TMCommandBar) {
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;