deleted python-code & migration-info for backupcodes
authorPhiTux <redacted>
Fri, 3 Oct 2025 17:34:42 +0000 (19:34 +0200)
committerPhiTux <redacted>
Fri, 3 Oct 2025 17:34:42 +0000 (19:34 +0200)
backend-python/.gitignore [deleted file]
backend-python/requirements.txt [deleted file]
backend-python/server/__init__.py [deleted file]
backend-python/server/main.py [deleted file]
backend-python/server/routers/__init__.py [deleted file]
backend-python/server/routers/logs.py [deleted file]
backend-python/server/routers/users.py [deleted file]
backend-python/server/utils/fileHandling.py [deleted file]
backend-python/server/utils/security.py [deleted file]
backend-python/server/utils/settings.py [deleted file]
frontend/src/routes/login/+page.svelte

diff --git a/backend-python/.gitignore b/backend-python/.gitignore
deleted file mode 100644 (file)
index fc8ad9f..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-.venv
-.envrc
-data/
-dev/
-*.pyc
-__pycache__/
\ No newline at end of file
diff --git a/backend-python/requirements.txt b/backend-python/requirements.txt
deleted file mode 100644 (file)
index a463667..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-fastapi[standard]
-pyjwt
-passlib[bcrypt]
-argon2_cffi
-cryptography
-pydantic-settings
\ No newline at end of file
diff --git a/backend-python/server/__init__.py b/backend-python/server/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/backend-python/server/main.py b/backend-python/server/main.py
deleted file mode 100644 (file)
index c117d0e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-from fastapi import FastAPI
-from .routers import users, logs
-from fastapi.middleware.cors import CORSMiddleware
-import logging
-from sys import stdout
-from .utils.settings import settings
-
-logger = logging.getLogger("dailytxtLogger")
-consoleHandler = logging.StreamHandler(stdout)
-consoleHandler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
-logger.addHandler(consoleHandler)
-logger.setLevel(logging.DEBUG)
-
-app = FastAPI()
-
-origins = settings.allowed_hosts
-
-app.add_middleware(
-    CORSMiddleware,
-    allow_origins=origins,
-    allow_credentials=True,
-    allow_methods=["*"],
-    allow_headers=["*"],
-)
-
-app.include_router(users.router, prefix="/users")
-app.include_router(logs.router, prefix="/logs")
-
-
-logger.info("Server started")
\ No newline at end of file
diff --git a/backend-python/server/routers/__init__.py b/backend-python/server/routers/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/backend-python/server/routers/logs.py b/backend-python/server/routers/logs.py
deleted file mode 100644 (file)
index 2defafa..0000000
+++ /dev/null
@@ -1,638 +0,0 @@
-import datetime
-import logging
-import re
-from fastapi import APIRouter, Depends, Form, UploadFile, File, HTTPException
-from fastapi.responses import StreamingResponse
-from pydantic import BaseModel
-from . import users
-from ..utils import fileHandling
-from ..utils import security
-import html
-from typing import Annotated
-
-
-logger = logging.getLogger("dailytxtLogger")
-
-router = APIRouter()
-
-
-class Log(BaseModel):
-    day: int
-    month: int
-    year: int
-    text: str
-    date_written: str
-
-@router.post("/saveLog")
-async def saveLog(log: Log, cookie = Depends(users.isLoggedIn)):
-    year = log.year
-    month = log.month
-    day = log.day
-
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
-    
-    history_available = False
-    # move old log to history
-    if "days" in content.keys():
-        for dayLog in content["days"]:
-            if dayLog["day"] == day and "text" in dayLog.keys():
-                history_available = True
-                historyVersion = 0
-                if "history" not in dayLog.keys():
-                    dayLog["history"] = []
-                else:
-                    for historyLog in dayLog["history"]:
-                        if historyLog["version"] > historyVersion:
-                            historyVersion = historyLog["version"]
-                historyVersion += 1
-                dayLog["history"].append({"version": historyVersion, "text": dayLog["text"], "date_written": dayLog["date_written"]})
-                break
-        
-    # save new log
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    ''' IMPORTANT: 
-    Escaping html characters here is NOT possible, since it would break the appearance
-    of html-code in the EDITOR. Code inside a markdown code-quote (`...`) will be auto-escaped.
-    Not a perfect solution, but actually any user can only load its own logs (and so XSS himself)...
-    '''
-    encrypted_text = security.encrypt_text(log.text, enc_key)
-    encrypted_date_written = security.encrypt_text(html.escape(log.date_written), enc_key)
-    
-    if "days" not in content.keys():
-        content["days"] = []
-        content["days"].append({"day": day, "text": encrypted_text, "date_written": encrypted_date_written})
-    else:
-        found = False
-        for dayLog in content["days"]:
-            if dayLog["day"] == day:
-                dayLog["text"] = encrypted_text
-                dayLog["date_written"] = encrypted_date_written
-                found = True
-                break
-        if not found:
-            content["days"].append({"day": day, "text": encrypted_text, "date_written": encrypted_date_written})
-
-    if not fileHandling.writeMonth(cookie["user_id"], year, month, content):
-        logger.error(f"Failed to save log for {cookie['user_id']} on {year}-{month:02d}-{day:02d}")
-        return {"success": False}
-
-    return {"success": True, "history_available": history_available}
-
-
-@router.get("/getLog")
-async def getLog(day: int, month: int, year: int, cookie = Depends(users.isLoggedIn)):
-    
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
-    
-    dummy = {"text": "", "date_written": "", "files": [], "tags": []}
-
-    if "days" not in content.keys():
-        return dummy
-    
-    for dayLog in content["days"]:
-        if dayLog["day"] == day:
-            enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-            text = ""
-            date_written = ""
-            history_available = False
-            if "text" in dayLog.keys():
-                text = security.decrypt_text(dayLog["text"], enc_key)
-                date_written = security.decrypt_text(dayLog["date_written"], enc_key)
-            if "files" in dayLog.keys():
-                for file in dayLog["files"]:
-                    file["filename"] = security.decrypt_text(file["enc_filename"], enc_key)
-            if "history" in dayLog.keys() and len(dayLog["history"]) > 0:
-                history_available = True
-            return {"text": text, "date_written": date_written, "files": dayLog.get("files", []), "tags": dayLog.get("tags", []), "history_available": history_available}
-
-    return dummy
-
-def get_start_index(text, index):
-    # find a whitespace two places before the index
-    
-    if index == 0:
-        return 0
-    
-    for i in range(3):
-        startIndex = text.rfind(" ", 0, index-1)
-        index = startIndex
-        if startIndex == -1:
-            return 0
-
-    return startIndex + 1
-
-def get_end_index(text, index):
-    # find a whitespace two places after the index
-    
-    if index == len(text) - 1:
-        return len(text)
-    
-    for i in range(3):
-        endIndex = text.find(" ", index+1)
-        index = endIndex
-        if endIndex == -1:
-            return len(text)
-
-    return endIndex
-
-
-def get_begin(text):
-    # get first 5 words
-    words = text.split()
-    if len(words) < 5:
-        return text
-    return " ".join(words[:5])
-
-def get_context(text: str, searchString: str, exact: bool):
-    # replace whitespace with non-breaking space
-    text = re.sub(r'\s+', " ", text)
-
-    if exact:
-        pos = text.find(searchString)
-    else:
-        pos = text.lower().find(searchString.lower())
-    if pos == -1:
-        return "<em>Dailytxt: Error formatting...</em>"
-
-    start = get_start_index(text, pos)
-    end = get_end_index(text, pos + len(searchString) - 1)
-    return text[start:pos] + "<b>" + text[pos:pos+len(searchString)] + "</b>" + text[pos+len(searchString):end]
-
-
-@router.get("/searchString")
-async def searchString(searchString: str, cookie = Depends(users.isLoggedIn)):
-    results = []
-    
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    # search in all years and months (dirs)
-    for year in fileHandling.get_years(cookie["user_id"]):
-        for month in fileHandling.get_months(cookie["user_id"], year):
-            content:dict = fileHandling.getMonth(cookie["user_id"], year, int(month))
-            if "days" not in content.keys():
-                continue
-            for dayLog in content["days"]:
-                if "text" not in dayLog.keys():
-                    if "files" in dayLog.keys():
-                        for file in dayLog["files"]:
-                            filename = security.decrypt_text(file["enc_filename"], enc_key)
-                            if searchString.lower() in filename.lower():
-                                context = "📎 " + filename
-                                results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-                                break
-                    continue
-                
-                text = security.decrypt_text(dayLog["text"], enc_key)
-                
-                # "..." -> exact
-                # ... | ... -> or
-                # ...  ... -> and
-
-                if searchString.startswith('"') and searchString.endswith('"'):
-                    if searchString[1:-1] in text:
-                        context = get_context(text, searchString[1:-1], True)
-                        results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-                        
-                
-                elif "|" in searchString:
-                    words = searchString.split("|")
-                    for word in words:
-                        if word.strip().lower() in text.lower():
-                            context = get_context(text, word.strip(), False)
-                            results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-                            break
-                            
-
-                elif " " in searchString:
-                    if all([word.strip().lower() in text.lower() for word in searchString.split()]):
-                        context = get_context(text, searchString.split()[0].strip(), False)
-                        results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-                        
-                
-                else:
-                    if searchString.lower() in text.lower():
-                        context = get_context(text, searchString, False)
-                        results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-                    
-                    elif "files" in dayLog.keys():
-                        for file in dayLog["files"]:
-                            filename = security.decrypt_text(file["enc_filename"], enc_key)
-                            if searchString.lower() in filename.lower():
-                                context = "📎 " + filename
-                                results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-                                break
-                        
-        
-    # sort by year and month and day
-    results.sort(key=lambda x: (int(x["year"]), int(x["month"]), int(x["day"])), reverse=False)
-    return results
-
-@router.get("/searchTag")
-async def searchTag(tag_id: int, cookie = Depends(users.isLoggedIn)):
-    results = []
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-
-    # search in all years and months (dirs)
-    for year in fileHandling.get_years(cookie["user_id"]):
-        for month in fileHandling.get_months(cookie["user_id"], year):
-            content:dict = fileHandling.getMonth(cookie["user_id"], year, int(month))
-            if "days" not in content.keys():
-                continue
-            for dayLog in content["days"]:
-                if "tags" not in dayLog.keys():
-                    continue
-                if tag_id in dayLog["tags"]:
-                    context = ''
-                    if "text" in dayLog.keys():
-                        text = security.decrypt_text(dayLog["text"], enc_key)
-                        context = get_begin(text)
-                    results.append({"year": year, "month": month, "day": dayLog["day"], "text": context})
-    
-    # sort by year and month and day
-    results.sort(key=lambda x: (int(x["year"]), int(x["month"]), int(x["day"])), reverse=False)
-    return results
-
-@router.get("/getMarkedDays")
-async def getMarkedDays(month: str, year: str, cookie = Depends(users.isLoggedIn)):
-    days_with_logs = []
-    days_with_files = []
-    days_bookmarked = []
-
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, int(month))
-    if "days" not in content.keys():
-        return {"days_with_logs": [], "days_with_files": [], "days_bookmarked": []}
-
-    for dayLog in content["days"]:
-        if "text" in dayLog.keys():
-            days_with_logs.append(dayLog["day"])
-        if "files" in dayLog.keys() and len(dayLog["files"]) > 0:
-            days_with_files.append(dayLog["day"])
-        if "bookmarked" in dayLog.keys() and dayLog["bookmarked"]:
-            days_bookmarked.append(dayLog["day"])
-    
-    return {"days_with_logs": days_with_logs, "days_with_files": days_with_files, "days_bookmarked": days_bookmarked}
-
-
-@router.get("/loadMonthForReading")
-async def loadMonthForReading(month: int, year: int, cookie = Depends(users.isLoggedIn)):
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
-    if "days" not in content.keys():
-        return []
-    
-    days = []
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    for dayLog in content["days"]:
-        day = {"day": dayLog["day"]}
-        if "text" in dayLog.keys():
-            day["text"] = security.decrypt_text(dayLog["text"], enc_key)
-            day["date_written"] = security.decrypt_text(dayLog["date_written"], enc_key)
-        if "tags" in dayLog.keys():
-            day["tags"] = dayLog["tags"]
-        if "files" in dayLog.keys():
-            day["files"] = []
-            for file in dayLog["files"]:
-                file["filename"] = security.decrypt_text(file["enc_filename"], enc_key)
-                day["files"].append(file)
-
-        # if one of the keys is in day: 
-        if "text" in day or "files" in day or "tags" in day:
-            days.append(day)
-    
-    days.sort(key=lambda x: x["day"])
-
-    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.getMonth(cookie["user_id"], year, month)
-
-    enc_filename = security.encrypt_text(file.filename, enc_key)
-    new_file = {"enc_filename": enc_filename, "uuid_filename": uuid, "size": file.size}
-
-    if "days" not in content.keys():
-        content["days"] = []
-        content["days"].append({"day": day, "files": [new_file]})
-
-    else:
-        found = False
-        for dayLog in content["days"]:
-            if dayLog["day"] == day:
-                if "files" not in dayLog.keys():
-                    dayLog["files"] = []
-                dayLog["files"].append(new_file)
-                found = True
-                break
-        if not found:
-            content["days"].append({"day": day, "files": [new_file]})
-    
-    if not fileHandling.writeMonth(cookie["user_id"], year, month, content):
-        fileHandling.removeFile(cookie["user_id"], uuid)
-        return {"success": False}
-
-    return {"success": True}
-
-
-@router.get("/deleteFile")
-async def deleteFile(uuid: str, day: int, month: int, year: int, cookie = Depends(users.isLoggedIn)):
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
-    if "days" not in content.keys():
-        raise HTTPException(status_code=500, detail="Day not found - json error")
-    
-    for dayLog in content["days"]:
-        if dayLog["day"] != day:
-            continue
-        if not "files" in dayLog.keys():
-            break
-        for file in dayLog["files"]:
-            if file["uuid_filename"] == uuid:
-                if not fileHandling.removeFile(cookie["user_id"], uuid):
-                    raise HTTPException(status_code=500, detail="Failed to delete file")
-                dayLog["files"].remove(file)
-                if not fileHandling.writeMonth(cookie["user_id"], year, month, content):
-                    raise HTTPException(status_code=500, detail="Failed to write changes of deleted file!")
-                return {"success": True}
-
-    raise HTTPException(status_code=500, detail="Failed to delete file - not found in log")
-
-@router.get("/downloadFile")
-async def downloadFile(uuid: str, cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    file = fileHandling.readFile(cookie["user_id"], uuid)
-    if file is None:
-        raise HTTPException(status_code=500, detail="Failed to read file")
-    content = security.decrypt_file(file, enc_key)
-    return StreamingResponse(iter([content]))
-
-@router.get("/getTags")
-async def getTags(cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    content:dict = fileHandling.getTags(cookie["user_id"])
-
-    if not 'tags' in content:
-        return []
-    
-    else:
-        for tag in content['tags']:
-            tag['icon'] = security.decrypt_text(tag['icon'], enc_key)
-            tag['name'] = security.decrypt_text(tag['name'], enc_key)
-            tag['color'] = security.decrypt_text(tag['color'], enc_key)
-        return content['tags']
-
-
-class NewTag(BaseModel):
-    icon: str
-    name: str
-    color: str
-
-@router.post("/saveNewTag")
-async def saveNewTag(tag: NewTag, cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    content:dict = fileHandling.getTags(cookie["user_id"])
-    
-    if not 'tags' in content:
-        content['tags'] = []
-        content['next_id'] = 1
-    
-    enc_icon = security.encrypt_text(tag.icon, enc_key)
-    enc_name = security.encrypt_text(tag.name, enc_key)
-    enc_color = security.encrypt_text(tag.color, enc_key)
-
-    new_tag = {"id": content['next_id'], "icon": enc_icon, "name": enc_name, "color": enc_color}
-    content['next_id'] += 1
-    content['tags'].append(new_tag)
-
-    if not fileHandling.writeTags(cookie["user_id"], content):
-        return {"success": False}
-    else:
-        return {"success": True}
-
-
-class EditTag(BaseModel):
-    id: int
-    icon: str
-    name: str
-    color: str
-
-@router.post("/editTag")
-async def editTag(editTag: EditTag, cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    content:dict = fileHandling.getTags(cookie["user_id"])
-    
-    if not 'tags' in content:
-        raise HTTPException(status_code=500, detail="Tag not found - json error")
-    
-    for tag in content['tags']:
-        if tag['id'] == editTag.id:
-            tag['icon'] = security.encrypt_text(editTag.icon, enc_key)
-            tag['name'] = security.encrypt_text(editTag.name, enc_key)
-            tag['color'] = security.encrypt_text(editTag.color, enc_key)
-            if not fileHandling.writeTags(cookie["user_id"], content):
-                raise HTTPException(status_code=500, detail="Failed to write tag - error writing tags")
-            else:
-                return {"success": True}
-    
-    raise HTTPException(status_code=500, detail="Tag not found - not in tags")
-
-@router.get("/deleteTag")
-async def deleteTag(id: int, cookie = Depends(users.isLoggedIn)):
-    # remove from every log if present
-    for year in fileHandling.get_years(cookie["user_id"]):
-        for month in fileHandling.get_months(cookie["user_id"], year):
-            content:dict = fileHandling.getMonth(cookie["user_id"], year, int(month))
-            if "days" not in content.keys():
-                continue
-            for dayLog in content["days"]:
-                if "tags" in dayLog.keys() and id in dayLog["tags"]:
-                    dayLog["tags"].remove(id)
-            if not fileHandling.writeMonth(cookie["user_id"], year, int(month), content):
-                raise HTTPException(status_code=500, detail="Failed to delete tag - error writing log")
-    
-    # remove from tags
-    content:dict = fileHandling.getTags(cookie["user_id"])
-    if not 'tags' in content:
-        raise HTTPException(status_code=500, detail="Tag not found - json error")
-    
-    for tag in content['tags']:
-        if tag['id'] == id:
-            content['tags'].remove(tag)
-            if not fileHandling.writeTags(cookie["user_id"], content):
-                raise HTTPException(status_code=500, detail="Failed to delete tag - error writing tags")
-            else:
-                return {"success": True}
-    
-    raise HTTPException(status_code=500, detail="Tag not found - not in tags")
-
-class AddTagToLog(BaseModel):
-    day: int
-    month: int
-    year: int
-    tag_id: int
-
-@router.post("/addTagToLog")
-async def addTagToLog(data: AddTagToLog, cookie = Depends(users.isLoggedIn)):
-    content:dict = fileHandling.getMonth(cookie["user_id"], data.year, data.month)
-    if "days" not in content.keys():
-        content["days"] = []
-    
-    dayFound = False
-    for dayLog in content["days"]:
-        if dayLog["day"] != data.day:
-            continue
-        dayFound = True
-        if not "tags" in dayLog.keys():
-            dayLog["tags"] = []
-        if data.tag_id in dayLog["tags"]:
-            # fail silently
-            return {"success": True}
-        dayLog["tags"].append(data.tag_id)
-        break
-    
-    if not dayFound:
-        content["days"].append({"day": data.day, "tags": [data.tag_id]})
-    
-    if not fileHandling.writeMonth(cookie["user_id"], data.year, data.month, content):
-        raise HTTPException(status_code=500, detail="Failed to write tag - error writing log")
-    return {"success": True}
-
-@router.post("/removeTagFromLog")
-async def removeTagFromLog(data: AddTagToLog, cookie = Depends(users.isLoggedIn)):
-    content:dict = fileHandling.getMonth(cookie["user_id"], data.year, data.month)
-    if "days" not in content.keys():
-        raise HTTPException(status_code=500, detail="Day not found - json error")
-    
-    for dayLog in content["days"]:
-        if dayLog["day"] != data.day:
-            continue
-        if not "tags" in dayLog.keys():
-            raise HTTPException(status_code=500, detail="Failed to remove tag - not found in log")
-        if not data.tag_id in dayLog["tags"]:
-            raise HTTPException(status_code=500, detail="Failed to remove tag - not found in log")
-        dayLog["tags"].remove(data.tag_id)
-        if not fileHandling.writeMonth(cookie["user_id"], data.year, data.month, content):
-            raise HTTPException(status_code=500, detail="Failed to remove tag - error writing log")
-        return {"success": True}
-    
-@router.get("/getTemplates")
-async def getTemplates(cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    content:dict = fileHandling.getTemplates(cookie["user_id"])
-
-    if not 'templates' in content:
-        return []
-    
-    else:
-        for template in content['templates']:
-            template['name'] = security.decrypt_text(template['name'], enc_key)
-            template['text'] = security.decrypt_text(template['text'], enc_key)
-        return content['templates']
-
-class Templates(BaseModel):
-    templates: list[dict]
-
-@router.post("/saveTemplates")
-async def saveTemplates(templates: Templates, cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    content = {'templates': []}
-    
-    for template in templates.templates:
-        enc_name = security.encrypt_text(template["name"], enc_key)
-        enc_text = security.encrypt_text(template["text"], enc_key)
-
-        new_template = {"name": enc_name, "text": enc_text}
-        content['templates'].append(new_template)
-
-    if not fileHandling.writeTemplates(cookie["user_id"], content):
-        raise HTTPException(status_code=500, detail="Failed to write templates - error writing templates")
-    else:
-        return {"success": True}
-
-
-@router.get("/getOnThisDay")
-async def getOnThisDay(day: int, month: int, year: int, last_years: str, cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    results = []
-
-    old_years = [year - int(y) for y in last_years.split(",") if y.isdigit()]
-
-    for old_year in old_years:
-        content:dict = fileHandling.getMonth(cookie["user_id"], old_year, month)
-
-        try:
-            for day_content in content['days']:
-                if day_content['day'] == day:
-                    text = security.decrypt_text(day_content['text'], enc_key) if 'text' in day_content else ''
-                    if text == '':
-                        continue
-                    results.append({'years_old': year - old_year, 'day': day, 'month': month, 'year': old_year, 'text': text})
-                    break
-        except:
-            continue
-        
-    return results
-
-
-@router.get("/getHistory")
-async def getHistory(day: int, month: int, year: int, cookie = Depends(users.isLoggedIn)):
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
-    
-    if "days" not in content.keys():
-        return []
-
-    for dayLog in content["days"]:
-        if dayLog["day"] == day and "history" in dayLog.keys():
-            history = []
-            for historyLog in dayLog["history"]:
-                history.append({
-                    "text": security.decrypt_text(historyLog["text"], enc_key),
-                    "date_written": security.decrypt_text(historyLog["date_written"], enc_key)
-                })
-            return history
-    
-    return []
-
-@router.get("/bookmarkDay")
-async def bookmarkDay(day: int, month: int, year: int, cookie = Depends(users.isLoggedIn)):
-    content:dict = fileHandling.getMonth(cookie["user_id"], year, month)
-    
-    if "days" not in content.keys():
-        content["days"] = []
-    
-    day_found = False
-    bookmarked = True
-    for dayLog in content["days"]:
-        if dayLog["day"] == day:
-            day_found = True
-            if "bookmarked" in dayLog and dayLog["bookmarked"]:
-                dayLog["bookmarked"] = False
-                bookmarked = False
-            else:
-                dayLog["bookmarked"] = True
-            break
-    
-    if not day_found:
-        content["days"].append({"day": day, "bookmarked": True})
-    
-    if not fileHandling.writeMonth(cookie["user_id"], year, month, content):
-        raise HTTPException(status_code=500, detail="Failed to bookmark day - error writing log")
-    
-    return {"success": True, "bookmarked": bookmarked}
\ No newline at end of file
diff --git a/backend-python/server/routers/users.py b/backend-python/server/routers/users.py
deleted file mode 100644 (file)
index b543c6f..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-import asyncio
-import datetime
-import json
-import secrets
-from fastapi import APIRouter, Cookie, Depends, HTTPException, Response
-from pydantic import BaseModel
-from ..utils import fileHandling
-from ..utils import security
-from ..utils.settings import settings
-import logging
-import base64
-import jwt
-
-logger = logging.getLogger("dailytxtLogger")
-
-router = APIRouter()
-
-class Login(BaseModel):
-    username: str
-    password: str
-
-@router.post("/login")
-async def login(login: Login, response: Response):
-
-    # check if user exists
-    content:dict = fileHandling.getUsers()
-    if len(content) == 0 or "users" not in content.keys() or len(content["users"]) == 0 or not any(user["username"] == login.username for user in content["users"]):
-        logger.error(f"Login failed. User '{login.username}' not found")
-        raise HTTPException(status_code=404, detail="User/Password combination not found")
-    
-    # get user data
-    user = next(user for user in content["users"] if user["username"] == login.username)
-    if not security.verify_password(login.password, user["password"]):
-        logger.error(f"Login failed. Password for user '{login.username}' is incorrect")
-        raise HTTPException(status_code=404, detail="User/Password combination not found")
-    
-    # get intermediate key
-    derived_key = base64.b64encode(security.derive_key_from_password(login.password, user["salt"])).decode()
-    
-    # build jwt
-    token = create_jwt(user["user_id"], user["username"], derived_key)
-    response.set_cookie(key="token", value=token, httponly=True, samesite="lax")
-    return {"username": user["username"]}
-
-def create_jwt(user_id, username, derived_key):
-    return jwt.encode({"exp": datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=settings.logout_after_days), "user_id": user_id, "name": username, "derived_key": derived_key}, settings.secret_token, algorithm="HS256")
-
-def decode_jwt(token):
-    return jwt.decode(token, settings.secret_token, algorithms="HS256")
-
-def isLoggedIn(token: str = Cookie()) -> int:
-    try:
-        decoded = decode_jwt(token)
-        return decoded
-    except jwt.ExpiredSignatureError:
-        raise HTTPException(status_code=440, detail="Token expired")
-    except:
-        raise HTTPException(status_code=401, detail="Not logged in")
-
-
-@router.get("/logout")
-def logout(response: Response):
-    response.delete_cookie("token", httponly=True)
-    return {"success": True}
-
-
-class Register(BaseModel):
-    username: str
-    password: str
-
-@router.post("/register")
-async def register(register: Register):
-    content:dict = fileHandling.getUsers()
-
-    # check if username already exists
-    if len(content) > 0:
-        if ("users" not in content.keys()):
-            logger.error("users.json is not in the correct format. Key 'users' is missing.")
-            raise HTTPException(status_code=500, detail="users.json is not in the correct format")
-        for user in content["users"]:
-            if user["username"] == register.username:
-                logger.error(f"Registration failed. Username '{register.username}' already exists")
-                raise HTTPException(status_code=400, detail="Username already exists")
-
-    # create new user-data
-    username = register.username
-    password = security.hash_password(register.password)
-    salt = secrets.token_urlsafe(16)
-    enc_enc_key = security.create_new_enc_enc_key(register.password, salt).decode()
-    
-
-    if len(content) == 0:
-        content = {"id_counter": 1, "users": [
-            {
-                "user_id": 1,
-                "dailytxt_version": 2,
-                "username": username,
-                "password": password,
-                "salt": salt, 
-                "enc_enc_key": enc_enc_key
-            }
-        ]}
-
-
-    else:
-        content["id_counter"] += 1
-        content["users"].append(
-            {
-                "user_id": content["id_counter"],
-                "dailytxt_version": 2,
-                "username": username,
-                "password": password,
-                "salt": salt, 
-                "enc_enc_key": enc_enc_key
-            }
-        )
-
-    try:
-        fileHandling.writeUsers(content)
-    except Exception as e:
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to write users.json") from e
-    else:
-        return {"success": True}
-
-def get_default_user_settings():
-    return {
-        "autoloadImagesByDefault": False,
-        "setAutoloadImagesPerDevice": True,
-        "useOnThisDay": True,
-        "onThisDayYears": [1,5,10],
-        "useBrowserTimezone": True,
-        "timezone": "UTC"
-    }
-
-@router.get("/getUserSettings")
-async def get_user_settings(cookie = Depends(isLoggedIn)):
-    user_id = cookie["user_id"]
-    content_enc = fileHandling.getUserSettings(user_id)   
-
-    if len(content_enc) > 0:
-        # decrypt settings
-        enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-        content = json.loads(security.decrypt_text(content_enc, enc_key))
-    else:
-        content = {}
-
-    default = get_default_user_settings()
-
-    for key in default.keys():
-        if key not in content.keys():
-            content[key] = default[key]
-    
-    return content
-
-
-@router.post("/saveUserSettings")
-async def save_user_settings(settings: dict, cookie = Depends(isLoggedIn)):
-    user_id = cookie["user_id"]
-    content = fileHandling.getUserSettings(user_id)   
-    
-    enc_key = security.get_enc_key(cookie["user_id"], cookie["derived_key"])
-    if len(content) > 0:
-        # decrypt settings
-        content = json.loads(security.decrypt_text(content, enc_key))
-    else:
-        content = {}
-
-    # if content is empty dict
-    if content is None or len(content) == 0:
-        content = get_default_user_settings()
-    
-    # update settings
-    for key in settings.keys():
-        content[key] = settings[key]
-
-    # encrypt settings
-    content_enc = security.encrypt_text(json.dumps(content), enc_key)
-
-    try:
-        fileHandling.writeUserSettings(user_id, content_enc)
-    except Exception as e:
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to write users.json") from e
-    else:
-        return {"success": True}
diff --git a/backend-python/server/utils/fileHandling.py b/backend-python/server/utils/fileHandling.py
deleted file mode 100644 (file)
index 608d688..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-import json
-import os
-import logging
-
-from fastapi import HTTPException
-from .settings import settings
-
-logger = logging.getLogger("dailytxtLogger")
-
-def getUsers():
-    try:
-        f = open(os.path.join(settings.data_path, "users.json"), "r")
-    except FileNotFoundError:
-        logger.info("users.json - File not found")
-        return {}
-    except Exception as e:
-        logger.exception(e)
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to open users.json")
-    else:
-        with f:
-            s = f.read()
-            if s == "":
-                return {}
-            return json.loads(s)
-
-def getMonth(user_id, year, month):
-    try:
-        f = open(os.path.join(settings.data_path, f"{user_id}/{year}/{month:02d}.json"), "r")
-    except FileNotFoundError:
-        logger.debug(f"{user_id}/{year}/{month:02d}.json - File not found")
-        return {}
-    except Exception as e:
-        logger.exception(e)
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to open {year}-{month}.json")
-    else:
-        with f:
-            s = f.read()
-            if s == "":
-                return {}
-            return json.loads(s)
-
-def writeUsers(content):
-    # print working directory
-    try:
-        f = open(os.path.join(settings.data_path, "users.json"), "w")
-    except Exception as e:
-        logger.exception(e)
-        return e
-    else:
-        with f:
-            f.write(json.dumps(content, indent=settings.indent))
-            return True
-        
-def writeMonth(user_id, year, month, content):
-    try:
-        os.makedirs(os.path.join(settings.data_path, f"{user_id}/{year}"), exist_ok=True)
-        f = open(os.path.join(settings.data_path, f"{user_id}/{year}/{month:02d}.json"), "w")
-    except Exception as e:
-        logger.exception(e)
-        return False
-    else:
-        with f:
-            f.write(json.dumps(content, indent=settings.indent))
-            return True
-        
-def get_years(user_id):
-    for entry in os.scandir(os.path.join(settings.data_path, str(user_id))):
-        if entry.is_dir() and entry.name.isnumeric() and len(entry.name) == 4:
-            yield entry.name
-
-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]
-
-def writeFile(file, 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(file)
-            return True
-        
-def readFile(user_id, uuid):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), 'files', uuid), "r")
-    except FileNotFoundError:
-        logger.info(f"{user_id}/files/{uuid} - File not found")
-        raise HTTPException(status_code=404, detail="File not found")
-    except Exception as e:
-        logger.exception(e)
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to open file {uuid}")
-    else:
-        with f:
-            return f.read()
-        
-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
-    
-def getTags(user_id):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), "tags.json"), "r")
-    except FileNotFoundError:
-        logger.info(f"{user_id}/tags.json - File not found")
-        return {}
-    except Exception as e:
-        logger.exception(e)
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to open tags.json")
-    else:
-        with f:
-            s = f.read()
-            if s == "":
-                return {}
-            return json.loads(s)
-        
-def writeTags(user_id, content):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), "tags.json"), "w")
-    except Exception as e:
-        logger.exception(e)
-        return False
-    else:
-        with f:
-            f.write(json.dumps(content, indent=settings.indent))
-            return True
-        
-def getUserSettings(user_id):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), "settings.encrypted"), "r")
-    except FileNotFoundError:
-        logger.info(f"{user_id}/settings.encrypted - File not found")
-        return {}
-    except Exception as e:
-        logger.exception(e)
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to open settings.json")
-    else:
-        with f:
-            s = f.read()
-            return s
-        
-def writeUserSettings(user_id, content):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), "settings.encrypted"), "w")
-    except Exception as e:
-        logger.exception(e)
-        return False
-    else:
-        with f:
-            f.write(content)
-            return True
-        
-def getTemplates(user_id):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), "templates.json"), "r")
-    except FileNotFoundError:
-        logger.info(f"{user_id}/templates.json - File not found")
-        return {}
-    except Exception as e:
-        logger.exception(e)
-        raise HTTPException(status_code=500, detail="Internal Server Error when trying to open templates.json")
-    else:
-        with f:
-            s = f.read()
-            if s == "":
-                return {}
-            return json.loads(s)
-        
-def writeTemplates(user_id, content):
-    try:
-        f = open(os.path.join(settings.data_path, str(user_id), "templates.json"), "w")
-    except Exception as e:
-        logger.exception(e)
-        return False
-    else:
-        with f:
-            f.write(json.dumps(content, indent=settings.indent))
-            return True
\ No newline at end of file
diff --git a/backend-python/server/utils/security.py b/backend-python/server/utils/security.py
deleted file mode 100644 (file)
index aec5102..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-from fastapi import HTTPException
-from passlib.hash import argon2
-from argon2.low_level import hash_secret_raw, Type
-from cryptography.fernet import Fernet
-import base64
-from . import fileHandling
-
-def hash_password(password: str) -> str:
-    return argon2.hash(password)
-
-def verify_password(password: str, hash: str) -> bool:
-    return argon2.verify(password, hash)
-
-def derive_key_from_password(password: str, salt: str) -> bytes:
-    return hash_secret_raw(secret=password.encode(), salt=salt.encode(), time_cost=2, memory_cost=2**15, parallelism=1, hash_len=32, type=Type.ID)
-
-def create_new_enc_enc_key(password: str, salt: str) -> bytes:
-    derived_key = derive_key_from_password(password, salt) # password derived key only to encrypt the actual encryption key
-    key = Fernet.generate_key() # actual encryption key
-    f = Fernet(base64.urlsafe_b64encode(derived_key))
-    return f.encrypt(key)
-
-def get_enc_key(user_id: int, derived_key: str) -> bytes:
-    content = fileHandling.getUsers()
-    
-    if not "users" in content.keys():
-        raise HTTPException(status_code=500, detail="users.json is not in the correct format. Key 'users' is missing.")
-    
-    for user in content["users"]:
-        if user["user_id"] == user_id:
-            key = user["enc_enc_key"]
-    
-            f = Fernet(base64.urlsafe_b64encode(base64.b64decode(derived_key)))
-            return base64.urlsafe_b64encode(base64.urlsafe_b64decode(f.decrypt(key)))
-
-def encrypt_text(text: str, key: str) -> str:
-    f = Fernet(key)
-    return f.encrypt(text.encode()).decode()
-
-def decrypt_text(text: str, key: str) -> str:
-    f = Fernet(key)
-    return f.decrypt(text.encode()).decode()
-
-def encrypt_file(file: bytes, key: str) -> str:
-    f = Fernet(key)
-    return f.encrypt(file).decode()
-
-def decrypt_file(file: str, key: str) -> bytes:
-    f = Fernet(key)
-    return f.decrypt(file.encode())
\ No newline at end of file
diff --git a/backend-python/server/utils/settings.py b/backend-python/server/utils/settings.py
deleted file mode 100644 (file)
index 5eb1691..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-import secrets
-from pydantic_settings import BaseSettings
-
-class Settings(BaseSettings):
-  data_path: str = "/data"
-  development: bool = False
-  secret_token: str = secrets.token_urlsafe(32) 
-  logout_after_days: int = 30
-  allowed_hosts: list[str] = ["http://localhost:5173","http://127.0.0.1:5173"]
-  indent: int | None = None
-
-settings = Settings()
\ No newline at end of file
index 379a9a44bd52d0236b7d6d57200d576dec9ecb2c..2021701ca5a054a3cfc2a317441a03e4f5ac8844 100644 (file)
                                                                                                })}
                                                                                        </div>
                                                                                {/if}
+                                                                               <div class="text-bg-info p-2 my-2 rounded">
+                                                                                       {@html $t('login.migration.account_info')}
+                                                                               </div>
                                                                        {/if}
                                                                </div>
                                                        {/if}
git clone https://git.99rst.org/PROJECT