From: PhiTux Date: Wed, 16 Jul 2025 17:31:55 +0000 (+0200) Subject: template migration working + remove settings on logout X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=7e784affaa7d5398f0dbd92750f8ca1d89410186;p=DailyTxT.git template migration working + remove settings on logout --- diff --git a/backend/handlers/additional.go b/backend/handlers/additional.go index 42da002..31dfcc6 100644 --- a/backend/handlers/additional.go +++ b/backend/handlers/additional.go @@ -5,6 +5,10 @@ import ( "fmt" "io" "net/http" + "os" + "path/filepath" + "regexp" + "sort" "strconv" "strings" @@ -435,155 +439,6 @@ func RemoveTagFromLog(w http.ResponseWriter, r *http.Request) { }) } -// LoadMonthForReading handles loading a month for reading -func LoadMonthForReading(w http.ResponseWriter, r *http.Request) { - // Get user ID and derived key from context - userID, ok := r.Context().Value(utils.UserIDKey).(int) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - - // Get parameters from URL - monthStr := r.URL.Query().Get("month") - if monthStr == "" { - http.Error(w, "Missing month parameter", http.StatusBadRequest) - return - } - month, err := strconv.Atoi(monthStr) - if err != nil { - http.Error(w, "Invalid month parameter", http.StatusBadRequest) - return - } - - yearStr := r.URL.Query().Get("year") - if yearStr == "" { - http.Error(w, "Missing year parameter", http.StatusBadRequest) - return - } - year, err := strconv.Atoi(yearStr) - if err != nil { - http.Error(w, "Invalid year parameter", http.StatusBadRequest) - return - } - - // Get month data - content, err := utils.GetMonth(userID, year, month) - if err != nil { - http.Error(w, fmt.Sprintf("Error retrieving month data: %v", err), http.StatusInternalServerError) - return - } - - // Get encryption key - encKey, err := utils.GetEncryptionKey(userID, derivedKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) - return - } - - // Check if days exist - days, ok := content["days"].([]any) - if !ok { - utils.JSONResponse(w, http.StatusOK, []any{}) - return - } - - // Process days - result := []any{} - for _, dayInterface := range days { - day, ok := dayInterface.(map[string]any) - if !ok { - continue - } - - dayNum, ok := day["day"].(float64) - if !ok { - continue - } - - // Create result day - resultDay := map[string]any{ - "day": int(dayNum), - } - - // Decrypt text and date_written - if text, ok := day["text"].(string); ok && text != "" { - decryptedText, err := utils.DecryptText(text, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting text: %v", err), http.StatusInternalServerError) - return - } - resultDay["text"] = decryptedText - - if dateWritten, ok := day["date_written"].(string); ok && dateWritten != "" { - decryptedDate, err := utils.DecryptText(dateWritten, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting date_written: %v", err), http.StatusInternalServerError) - return - } - resultDay["date_written"] = decryptedDate - } - } - - // Get tags - if tags, ok := day["tags"].([]any); ok && len(tags) > 0 { - resultDay["tags"] = tags - } - - // Decrypt filenames if files exist - if filesList, ok := day["files"].([]any); ok && len(filesList) > 0 { - files := []any{} - for _, fileInterface := range filesList { - file, ok := fileInterface.(map[string]any) - if !ok { - continue - } - - if encFilename, ok := file["enc_filename"].(string); ok { - decryptedFilename, err := utils.DecryptText(encFilename, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting filename: %v", err), http.StatusInternalServerError) - return - } - fileCopy := make(map[string]any) - for k, v := range file { - fileCopy[k] = v - } - fileCopy["filename"] = decryptedFilename - files = append(files, fileCopy) - } - } - resultDay["files"] = files - } - - // Add day to result if it has content - if _, hasText := resultDay["text"]; hasText { - result = append(result, resultDay) - } else if _, hasFiles := resultDay["files"]; hasFiles { - result = append(result, resultDay) - } else if _, hasTags := resultDay["tags"]; hasTags { - result = append(result, resultDay) - } - } - - // Sort by day - /* - sort.Slice(result, func(i, j int) bool { - dayI := result[i].(map[string]any)["day"].(int) - dayJ := result[j].(map[string]any)["day"].(int) - return dayI < dayJ - }) - */ - - // Return result - utils.JSONResponse(w, http.StatusOK, result) -} - // UploadFile handles uploading a file func UploadFile(w http.ResponseWriter, r *http.Request) { // Get user ID and derived key from context @@ -940,140 +795,6 @@ func DeleteFile(w http.ResponseWriter, r *http.Request) { }) } -// GetHistory handles retrieving log history -func GetHistory(w http.ResponseWriter, r *http.Request) { - // Get user ID and derived key from context - userID, ok := r.Context().Value(utils.UserIDKey).(int) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - - // Get parameters - dayStr := r.URL.Query().Get("day") - if dayStr == "" { - http.Error(w, "Missing day parameter", http.StatusBadRequest) - return - } - day, err := strconv.Atoi(dayStr) - if err != nil { - http.Error(w, "Invalid day parameter", http.StatusBadRequest) - return - } - - monthStr := r.URL.Query().Get("month") - if monthStr == "" { - http.Error(w, "Missing month parameter", http.StatusBadRequest) - return - } - month, err := strconv.Atoi(monthStr) - if err != nil { - http.Error(w, "Invalid month parameter", http.StatusBadRequest) - return - } - - yearStr := r.URL.Query().Get("year") - if yearStr == "" { - http.Error(w, "Missing year parameter", http.StatusBadRequest) - return - } - year, err := strconv.Atoi(yearStr) - if err != nil { - http.Error(w, "Invalid year parameter", http.StatusBadRequest) - return - } - - // Get encryption key - encKey, err := utils.GetEncryptionKey(userID, derivedKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) - return - } - - // Get month data - content, err := utils.GetMonth(userID, year, month) - if err != nil { - http.Error(w, fmt.Sprintf("Error retrieving month data: %v", err), http.StatusInternalServerError) - return - } - - // Check if days exist - days, ok := content["days"].([]any) - if !ok { - utils.JSONResponse(w, http.StatusOK, []any{}) - return - } - - // Find day - for _, dayInterface := range days { - dayObj, ok := dayInterface.(map[string]any) - if !ok { - continue - } - - dayNum, ok := dayObj["day"].(float64) - if !ok || int(dayNum) != day { - continue - } - - // Check for history - history, ok := dayObj["history"].([]any) - if !ok || len(history) == 0 { - utils.JSONResponse(w, http.StatusOK, []any{}) - return - } - - // Decrypt history entries - result := []any{} - for _, historyInterface := range history { - historyEntry, ok := historyInterface.(map[string]any) - if !ok { - continue - } - - text, ok := historyEntry["text"].(string) - if !ok { - continue - } - - dateWritten, ok := historyEntry["date_written"].(string) - if !ok { - continue - } - - // Decrypt text and date - decryptedText, err := utils.DecryptText(text, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting history text: %v", err), http.StatusInternalServerError) - return - } - - decryptedDate, err := utils.DecryptText(dateWritten, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting history date: %v", err), http.StatusInternalServerError) - return - } - - result = append(result, map[string]any{ - "text": decryptedText, - "date_written": decryptedDate, - }) - } - - // Return history - utils.JSONResponse(w, http.StatusOK, result) - return - } - - // Day not found - utils.JSONResponse(w, http.StatusOK, []any{}) -} - // BookmarkDay handles bookmarking a day func BookmarkDay(w http.ResponseWriter, r *http.Request) { // Get user ID from context @@ -1146,11 +867,11 @@ func BookmarkDay(w http.ResponseWriter, r *http.Request) { // Day found, toggle bookmark dayFound = true - if bookmark, ok := dayObj["bookmarked"].(bool); ok && bookmark { - dayObj["bookmarked"] = false + if bookmark, ok := dayObj["isBookmarked"].(bool); ok && bookmark { + dayObj["isBookmarked"] = false bookmarked = false } else { - dayObj["bookmarked"] = true + dayObj["isBookmarked"] = true } days[i] = dayObj break @@ -1159,8 +880,8 @@ func BookmarkDay(w http.ResponseWriter, r *http.Request) { if !dayFound { // Create new day with bookmark days = append(days, map[string]any{ - "day": day, - "bookmarked": true, + "day": day, + "isBookmarked": true, }) } @@ -1325,3 +1046,461 @@ func SearchTag(w http.ResponseWriter, r *http.Request) { // Return results utils.JSONResponse(w, http.StatusOK, results) } + +// GetTags handles retrieving a user's tags +func GetTags(w http.ResponseWriter, r *http.Request) { + // Get user ID and derived key from context + userID, ok := r.Context().Value(utils.UserIDKey).(int) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Get tags + content, err := utils.GetTags(userID) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving tags: %v", err), http.StatusInternalServerError) + return + } + + // If no tags, return empty array + if tags, ok := content["tags"].([]any); !ok || len(tags) == 0 { + utils.JSONResponse(w, http.StatusOK, []any{}) + return + } + + // Get encryption key + encKey, err := utils.GetEncryptionKey(userID, derivedKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) + return + } + + // Decrypt tag data + tags := content["tags"].([]any) + result := []any{} + + for _, tagInterface := range tags { + tag, ok := tagInterface.(map[string]any) + if !ok { + continue + } + + // Decrypt icon, name, and color + if encIcon, ok := tag["icon"].(string); ok { + decryptedIcon, err := utils.DecryptText(encIcon, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting tag icon: %v", err), http.StatusInternalServerError) + return + } + tag["icon"] = decryptedIcon + } + + if encName, ok := tag["name"].(string); ok { + decryptedName, err := utils.DecryptText(encName, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting tag name: %v", err), http.StatusInternalServerError) + return + } + tag["name"] = decryptedName + } + + if encColor, ok := tag["color"].(string); ok { + decryptedColor, err := utils.DecryptText(encColor, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting tag color: %v", err), http.StatusInternalServerError) + return + } + tag["color"] = decryptedColor + } + + result = append(result, tag) + } + + // Return tags + utils.JSONResponse(w, http.StatusOK, result) +} + +// TagRequest represents a tag request +type TagRequest struct { + Icon string `json:"icon"` + Name string `json:"name"` + Color string `json:"color"` +} + +// SaveTags handles saving a new tag +func SaveTags(w http.ResponseWriter, r *http.Request) { + // Get user ID and derived key from context + userID, ok := r.Context().Value(utils.UserIDKey).(int) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Parse request body + var req TagRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + // Get tags + content, err := utils.GetTags(userID) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving tags: %v", err), http.StatusInternalServerError) + return + } + + // Create tags array if it doesn't exist + if _, ok := content["tags"]; !ok { + content["tags"] = []any{} + } + if _, ok := content["next_id"]; !ok { + content["next_id"] = 1 + } + + // Get encryption key + encKey, err := utils.GetEncryptionKey(userID, derivedKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) + return + } + + // Encrypt tag data + encIcon, err := utils.EncryptText(req.Icon, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error encrypting tag icon: %v", err), http.StatusInternalServerError) + return + } + + encName, err := utils.EncryptText(req.Name, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error encrypting tag name: %v", err), http.StatusInternalServerError) + return + } + + encColor, err := utils.EncryptText(req.Color, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error encrypting tag color: %v", err), http.StatusInternalServerError) + return + } + + // Create new tag + nextID, ok := content["next_id"].(float64) + if !ok { + nextID = 1 + } + + newTag := map[string]any{ + "id": int(nextID), + "icon": encIcon, + "name": encName, + "color": encColor, + } + + // Add tag to tags array + tags, ok := content["tags"].([]any) + if !ok { + tags = []any{} + } + tags = append(tags, newTag) + content["tags"] = tags + content["next_id"] = nextID + 1 + + // Write tags + if err := utils.WriteTags(userID, content); err != nil { + http.Error(w, fmt.Sprintf("Error writing tags: %v", err), http.StatusInternalServerError) + return + } + + // Return success + utils.JSONResponse(w, http.StatusOK, map[string]bool{ + "success": true, + }) +} + +// Helper functions for search +func getStartIndex(text string, index int) int { + if index == 0 { + return 0 + } + + for i := 0; i < 3; i++ { + startIndex := strings.LastIndex(text[:index-1], " ") + index = startIndex + if startIndex == -1 { + return 0 + } + } + + return index + 1 +} + +func getEndIndex(text string, index int) int { + if index == len(text)-1 { + return len(text) + } + + for i := 0; i < 3; i++ { + endIndex := strings.Index(text[index+1:], " ") + if endIndex == -1 { + return len(text) + } + index = index + 1 + endIndex + } + + return index +} + +func getContext(text, searchString string, exact bool) string { + // Replace whitespace with non-breaking space + re := regexp.MustCompile(`\s+`) + text = re.ReplaceAllString(text, " ") + + var pos int + if exact { + pos = strings.Index(text, searchString) + } else { + pos = strings.Index(strings.ToLower(text), strings.ToLower(searchString)) + } + + if pos == -1 { + return "Dailytxt: Error formatting..." + } + + start := getStartIndex(text, pos) + end := getEndIndex(text, pos+len(searchString)-1) + return text[start:pos] + "" + text[pos:pos+len(searchString)] + "" + text[pos+len(searchString):end] +} + +// Search handles searching logs for text +func Search(w http.ResponseWriter, r *http.Request) { + // Get user ID and derived key from context + userID, ok := r.Context().Value(utils.UserIDKey).(int) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Get query parameter + searchString := r.URL.Query().Get("searchString") + if searchString == "" { + http.Error(w, "Missing search parameter", http.StatusBadRequest) + return + } + + // Get encryption key + encKey, err := utils.GetEncryptionKey(userID, derivedKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) + return + } + + // Get user directory + userDir := filepath.Join(utils.Settings.DataPath, strconv.Itoa(userID)) + results := []any{} + + // Traverse all years and months + yearEntries, err := os.ReadDir(userDir) + if err != nil { + http.Error(w, fmt.Sprintf("Error reading user directory: %v", err), http.StatusInternalServerError) + return + } + + // Regex to match year directories (4 digits) + yearRegex := regexp.MustCompile(`^\d{4}$`) + + for _, yearEntry := range yearEntries { + if !yearEntry.IsDir() || !yearRegex.MatchString(yearEntry.Name()) { + continue + } + year := yearEntry.Name() + + // Read month files in year directory + yearDir := filepath.Join(userDir, year) + monthEntries, err := os.ReadDir(yearDir) + if err != nil { + continue + } + + // Regex to match month files (2 digits + .json) + monthRegex := regexp.MustCompile(`^(\d{2})\.json$`) + + for _, monthEntry := range monthEntries { + if monthEntry.IsDir() { + continue + } + + matches := monthRegex.FindStringSubmatch(monthEntry.Name()) + if len(matches) != 2 { + continue + } + month := matches[1] + + // Get month content + monthInt, _ := strconv.Atoi(month) + yearInt, _ := strconv.Atoi(year) + content, err := utils.GetMonth(userID, yearInt, monthInt) + if err != nil { + continue + } + + days, ok := content["days"].([]any) + if !ok { + continue + } + + // Process each day + for _, dayInterface := range days { + dayLog, ok := dayInterface.(map[string]any) + if !ok { + continue + } + + dayNum, ok := dayLog["day"].(float64) + if !ok { + continue + } + day := int(dayNum) + + // Check text + if text, ok := dayLog["text"].(string); ok { + decryptedText, err := utils.DecryptText(text, encKey) + if err != nil { + continue + } + + // Apply search logic + if strings.HasPrefix(searchString, "\"") && strings.HasSuffix(searchString, "\"") { + // Exact match + searchTerm := searchString[1 : len(searchString)-1] + if strings.Contains(decryptedText, searchTerm) { + context := getContext(decryptedText, searchTerm, true) + results = append(results, map[string]any{ + "year": year, + "month": month, + "day": day, + "text": context, + }) + } + } else if strings.Contains(searchString, "|") { + // OR search + words := strings.Split(searchString, "|") + for _, word := range words { + wordTrimmed := strings.TrimSpace(word) + if strings.Contains(strings.ToLower(decryptedText), strings.ToLower(wordTrimmed)) { + context := getContext(decryptedText, wordTrimmed, false) + results = append(results, map[string]any{ + "year": year, + "month": month, + "day": day, + "text": context, + }) + break + } + } + } else if strings.Contains(searchString, " ") { + // AND search + words := strings.Split(searchString, " ") + allWordsMatch := true + for _, word := range words { + wordTrimmed := strings.TrimSpace(word) + if !strings.Contains(strings.ToLower(decryptedText), strings.ToLower(wordTrimmed)) { + allWordsMatch = false + break + } + } + if allWordsMatch { + context := getContext(decryptedText, strings.TrimSpace(words[0]), false) + results = append(results, map[string]any{ + "year": year, + "month": month, + "day": day, + "text": context, + }) + } + } else { + // Simple search + if strings.Contains(strings.ToLower(decryptedText), strings.ToLower(searchString)) { + context := getContext(decryptedText, searchString, false) + results = append(results, map[string]any{ + "year": year, + "month": month, + "day": day, + "text": context, + }) + } + } + } + + // Check filenames + if files, ok := dayLog["files"].([]any); ok { + for _, fileInterface := range files { + file, ok := fileInterface.(map[string]any) + if !ok { + continue + } + + if encFilename, ok := file["enc_filename"].(string); ok { + decryptedFilename, err := utils.DecryptText(encFilename, encKey) + if err != nil { + continue + } + + if strings.Contains(strings.ToLower(decryptedFilename), strings.ToLower(searchString)) { + context := "📎 " + decryptedFilename + results = append(results, map[string]any{ + "year": year, + "month": month, + "day": day, + "text": context, + }) + break + } + } + } + } + } + } + } + + // Sort results by date + sort.Slice(results, func(i, j int) bool { + ri := results[i].(map[string]any) + rj := results[j].(map[string]any) + + yearI, _ := strconv.Atoi(ri["year"].(string)) + yearJ, _ := strconv.Atoi(rj["year"].(string)) + if yearI != yearJ { + return yearI < yearJ + } + + monthI, _ := strconv.Atoi(ri["month"].(string)) + monthJ, _ := strconv.Atoi(rj["month"].(string)) + if monthI != monthJ { + return monthI < monthJ + } + + dayI := ri["day"].(int) + dayJ := rj["day"].(int) + return dayI < dayJ + }) + + // Return results + utils.JSONResponse(w, http.StatusOK, results) +} diff --git a/backend/handlers/logs.go b/backend/handlers/logs.go index e014e08..a611c04 100644 --- a/backend/handlers/logs.go +++ b/backend/handlers/logs.go @@ -5,10 +5,6 @@ import ( "fmt" "html" "net/http" - "os" - "path/filepath" - "regexp" - "sort" "strconv" "strings" @@ -381,7 +377,7 @@ func GetMarkedDays(w http.ResponseWriter, r *http.Request) { } // Check if bookmarked - if bookmarked, ok := day["bookmarked"].(bool); ok && bookmarked { + if bookmarked, ok := day["isBookmarked"].(bool); ok && bookmarked { daysBookmarked = append(daysBookmarked, int(dayNum)) } } @@ -395,188 +391,6 @@ func GetMarkedDays(w http.ResponseWriter, r *http.Request) { }) } -// GetTags handles retrieving a user's tags -func GetTags(w http.ResponseWriter, r *http.Request) { - // Get user ID and derived key from context - userID, ok := r.Context().Value(utils.UserIDKey).(int) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - - // Get tags - content, err := utils.GetTags(userID) - if err != nil { - http.Error(w, fmt.Sprintf("Error retrieving tags: %v", err), http.StatusInternalServerError) - return - } - - // If no tags, return empty array - if tags, ok := content["tags"].([]any); !ok || len(tags) == 0 { - utils.JSONResponse(w, http.StatusOK, []any{}) - return - } - - // Get encryption key - encKey, err := utils.GetEncryptionKey(userID, derivedKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) - return - } - - // Decrypt tag data - tags := content["tags"].([]any) - result := []any{} - - for _, tagInterface := range tags { - tag, ok := tagInterface.(map[string]any) - if !ok { - continue - } - - // Decrypt icon, name, and color - if encIcon, ok := tag["icon"].(string); ok { - decryptedIcon, err := utils.DecryptText(encIcon, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting tag icon: %v", err), http.StatusInternalServerError) - return - } - tag["icon"] = decryptedIcon - } - - if encName, ok := tag["name"].(string); ok { - decryptedName, err := utils.DecryptText(encName, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting tag name: %v", err), http.StatusInternalServerError) - return - } - tag["name"] = decryptedName - } - - if encColor, ok := tag["color"].(string); ok { - decryptedColor, err := utils.DecryptText(encColor, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error decrypting tag color: %v", err), http.StatusInternalServerError) - return - } - tag["color"] = decryptedColor - } - - result = append(result, tag) - } - - // Return tags - utils.JSONResponse(w, http.StatusOK, result) -} - -// TagRequest represents a tag request -type TagRequest struct { - Icon string `json:"icon"` - Name string `json:"name"` - Color string `json:"color"` -} - -// SaveTags handles saving a new tag -func SaveTags(w http.ResponseWriter, r *http.Request) { - // Get user ID and derived key from context - userID, ok := r.Context().Value(utils.UserIDKey).(int) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) - if !ok { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - - // Parse request body - var req TagRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "Invalid request body", http.StatusBadRequest) - return - } - - // Get tags - content, err := utils.GetTags(userID) - if err != nil { - http.Error(w, fmt.Sprintf("Error retrieving tags: %v", err), http.StatusInternalServerError) - return - } - - // Create tags array if it doesn't exist - if _, ok := content["tags"]; !ok { - content["tags"] = []any{} - } - if _, ok := content["next_id"]; !ok { - content["next_id"] = 1 - } - - // Get encryption key - encKey, err := utils.GetEncryptionKey(userID, derivedKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) - return - } - - // Encrypt tag data - encIcon, err := utils.EncryptText(req.Icon, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error encrypting tag icon: %v", err), http.StatusInternalServerError) - return - } - - encName, err := utils.EncryptText(req.Name, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error encrypting tag name: %v", err), http.StatusInternalServerError) - return - } - - encColor, err := utils.EncryptText(req.Color, encKey) - if err != nil { - http.Error(w, fmt.Sprintf("Error encrypting tag color: %v", err), http.StatusInternalServerError) - return - } - - // Create new tag - nextID, ok := content["next_id"].(float64) - if !ok { - nextID = 1 - } - - newTag := map[string]any{ - "id": int(nextID), - "icon": encIcon, - "name": encName, - "color": encColor, - } - - // Add tag to tags array - tags, ok := content["tags"].([]any) - if !ok { - tags = []any{} - } - tags = append(tags, newTag) - content["tags"] = tags - content["next_id"] = nextID + 1 - - // Write tags - if err := utils.WriteTags(userID, content); err != nil { - http.Error(w, fmt.Sprintf("Error writing tags: %v", err), http.StatusInternalServerError) - return - } - - // Return success - utils.JSONResponse(w, http.StatusOK, map[string]bool{ - "success": true, - }) -} - // TemplatesRequest represents a templates request type TemplatesRequest struct { Templates []struct { @@ -828,62 +642,148 @@ func GetOnThisDay(w http.ResponseWriter, r *http.Request) { utils.JSONResponse(w, http.StatusOK, results) } -// Helper functions for search -func getStartIndex(text string, index int) int { - if index == 0 { - return 0 +// LoadMonthForReading handles loading a month for reading +func LoadMonthForReading(w http.ResponseWriter, r *http.Request) { + // Get user ID and derived key from context + userID, ok := r.Context().Value(utils.UserIDKey).(int) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + derivedKey, ok := r.Context().Value(utils.DerivedKeyKey).(string) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return } - for i := 0; i < 3; i++ { - startIndex := strings.LastIndex(text[:index-1], " ") - index = startIndex - if startIndex == -1 { - return 0 - } + // Get parameters from URL + monthStr := r.URL.Query().Get("month") + if monthStr == "" { + http.Error(w, "Missing month parameter", http.StatusBadRequest) + return + } + month, err := strconv.Atoi(monthStr) + if err != nil { + http.Error(w, "Invalid month parameter", http.StatusBadRequest) + return } - return index + 1 -} + yearStr := r.URL.Query().Get("year") + if yearStr == "" { + http.Error(w, "Missing year parameter", http.StatusBadRequest) + return + } + year, err := strconv.Atoi(yearStr) + if err != nil { + http.Error(w, "Invalid year parameter", http.StatusBadRequest) + return + } -func getEndIndex(text string, index int) int { - if index == len(text)-1 { - return len(text) + // Get month data + content, err := utils.GetMonth(userID, year, month) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving month data: %v", err), http.StatusInternalServerError) + return } - for i := 0; i < 3; i++ { - endIndex := strings.Index(text[index+1:], " ") - if endIndex == -1 { - return len(text) - } - index = index + 1 + endIndex + // Get encryption key + encKey, err := utils.GetEncryptionKey(userID, derivedKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error getting encryption key: %v", err), http.StatusInternalServerError) + return } - return index -} + // Check if days exist + days, ok := content["days"].([]any) + if !ok { + utils.JSONResponse(w, http.StatusOK, []any{}) + return + } -func getContext(text, searchString string, exact bool) string { - // Replace whitespace with non-breaking space - re := regexp.MustCompile(`\s+`) - text = re.ReplaceAllString(text, " ") + // Process days + result := []any{} + for _, dayInterface := range days { + day, ok := dayInterface.(map[string]any) + if !ok { + continue + } - var pos int - if exact { - pos = strings.Index(text, searchString) - } else { - pos = strings.Index(strings.ToLower(text), strings.ToLower(searchString)) - } + dayNum, ok := day["day"].(float64) + if !ok { + continue + } + + // Create result day + resultDay := map[string]any{ + "day": int(dayNum), + } + + // Decrypt text and date_written + if text, ok := day["text"].(string); ok && text != "" { + decryptedText, err := utils.DecryptText(text, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting text: %v", err), http.StatusInternalServerError) + return + } + resultDay["text"] = decryptedText + + if dateWritten, ok := day["date_written"].(string); ok && dateWritten != "" { + decryptedDate, err := utils.DecryptText(dateWritten, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting date_written: %v", err), http.StatusInternalServerError) + return + } + resultDay["date_written"] = decryptedDate + } + } + + // Get tags + if tags, ok := day["tags"].([]any); ok && len(tags) > 0 { + resultDay["tags"] = tags + } - if pos == -1 { - return "Dailytxt: Error formatting..." + // Decrypt filenames if files exist + if filesList, ok := day["files"].([]any); ok && len(filesList) > 0 { + files := []any{} + for _, fileInterface := range filesList { + file, ok := fileInterface.(map[string]any) + if !ok { + continue + } + + if encFilename, ok := file["enc_filename"].(string); ok { + decryptedFilename, err := utils.DecryptText(encFilename, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting filename: %v", err), http.StatusInternalServerError) + return + } + fileCopy := make(map[string]any) + for k, v := range file { + fileCopy[k] = v + } + fileCopy["filename"] = decryptedFilename + files = append(files, fileCopy) + } + } + resultDay["files"] = files + } + + // Add day to result if it has content + if _, hasText := resultDay["text"]; hasText { + result = append(result, resultDay) + } else if _, hasFiles := resultDay["files"]; hasFiles { + result = append(result, resultDay) + } else if _, hasTags := resultDay["tags"]; hasTags { + result = append(result, resultDay) + } } - start := getStartIndex(text, pos) - end := getEndIndex(text, pos+len(searchString)-1) - return text[start:pos] + "" + text[pos:pos+len(searchString)] + "" + text[pos+len(searchString):end] + // Return result + utils.JSONResponse(w, http.StatusOK, result) } -// Search handles searching logs for text -func Search(w http.ResponseWriter, r *http.Request) { +// GetHistory handles retrieving log history +func GetHistory(w http.ResponseWriter, r *http.Request) { // Get user ID and derived key from context userID, ok := r.Context().Value(utils.UserIDKey).(int) if !ok { @@ -896,10 +796,37 @@ func Search(w http.ResponseWriter, r *http.Request) { return } - // Get query parameter - searchString := r.URL.Query().Get("searchString") - if searchString == "" { - http.Error(w, "Missing search parameter", http.StatusBadRequest) + // Get parameters + dayStr := r.URL.Query().Get("day") + if dayStr == "" { + http.Error(w, "Missing day parameter", http.StatusBadRequest) + return + } + day, err := strconv.Atoi(dayStr) + if err != nil { + http.Error(w, "Invalid day parameter", http.StatusBadRequest) + return + } + + monthStr := r.URL.Query().Get("month") + if monthStr == "" { + http.Error(w, "Missing month parameter", http.StatusBadRequest) + return + } + month, err := strconv.Atoi(monthStr) + if err != nil { + http.Error(w, "Invalid month parameter", http.StatusBadRequest) + return + } + + yearStr := r.URL.Query().Get("year") + if yearStr == "" { + http.Error(w, "Missing year parameter", http.StatusBadRequest) + return + } + year, err := strconv.Atoi(yearStr) + if err != nil { + http.Error(w, "Invalid year parameter", http.StatusBadRequest) return } @@ -910,196 +837,81 @@ func Search(w http.ResponseWriter, r *http.Request) { return } - // Get user directory - userDir := filepath.Join(utils.Settings.DataPath, strconv.Itoa(userID)) - results := []any{} - - // Traverse all years and months - yearEntries, err := os.ReadDir(userDir) + // Get month data + content, err := utils.GetMonth(userID, year, month) if err != nil { - http.Error(w, fmt.Sprintf("Error reading user directory: %v", err), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("Error retrieving month data: %v", err), http.StatusInternalServerError) return } - // Regex to match year directories (4 digits) - yearRegex := regexp.MustCompile(`^\d{4}$`) + // Check if days exist + days, ok := content["days"].([]any) + if !ok { + utils.JSONResponse(w, http.StatusOK, []any{}) + return + } - for _, yearEntry := range yearEntries { - if !yearEntry.IsDir() || !yearRegex.MatchString(yearEntry.Name()) { + // Find day + for _, dayInterface := range days { + dayObj, ok := dayInterface.(map[string]any) + if !ok { continue } - year := yearEntry.Name() - // Read month files in year directory - yearDir := filepath.Join(userDir, year) - monthEntries, err := os.ReadDir(yearDir) - if err != nil { + dayNum, ok := dayObj["day"].(float64) + if !ok || int(dayNum) != day { continue } - // Regex to match month files (2 digits + .json) - monthRegex := regexp.MustCompile(`^(\d{2})\.json$`) - - for _, monthEntry := range monthEntries { - if monthEntry.IsDir() { - continue - } + // Check for history + history, ok := dayObj["history"].([]any) + if !ok || len(history) == 0 { + utils.JSONResponse(w, http.StatusOK, []any{}) + return + } - matches := monthRegex.FindStringSubmatch(monthEntry.Name()) - if len(matches) != 2 { + // Decrypt history entries + result := []any{} + for _, historyInterface := range history { + historyEntry, ok := historyInterface.(map[string]any) + if !ok { continue } - month := matches[1] - // Get month content - monthInt, _ := strconv.Atoi(month) - yearInt, _ := strconv.Atoi(year) - content, err := utils.GetMonth(userID, yearInt, monthInt) - if err != nil { + text, ok := historyEntry["text"].(string) + if !ok { continue } - days, ok := content["days"].([]any) + dateWritten, ok := historyEntry["date_written"].(string) if !ok { continue } - // Process each day - for _, dayInterface := range days { - dayLog, ok := dayInterface.(map[string]any) - if !ok { - continue - } - - dayNum, ok := dayLog["day"].(float64) - if !ok { - continue - } - day := int(dayNum) - - // Check text - if text, ok := dayLog["text"].(string); ok { - decryptedText, err := utils.DecryptText(text, encKey) - if err != nil { - continue - } - - // Apply search logic - if strings.HasPrefix(searchString, "\"") && strings.HasSuffix(searchString, "\"") { - // Exact match - searchTerm := searchString[1 : len(searchString)-1] - if strings.Contains(decryptedText, searchTerm) { - context := getContext(decryptedText, searchTerm, true) - results = append(results, map[string]any{ - "year": year, - "month": month, - "day": day, - "text": context, - }) - } - } else if strings.Contains(searchString, "|") { - // OR search - words := strings.Split(searchString, "|") - for _, word := range words { - wordTrimmed := strings.TrimSpace(word) - if strings.Contains(strings.ToLower(decryptedText), strings.ToLower(wordTrimmed)) { - context := getContext(decryptedText, wordTrimmed, false) - results = append(results, map[string]any{ - "year": year, - "month": month, - "day": day, - "text": context, - }) - break - } - } - } else if strings.Contains(searchString, " ") { - // AND search - words := strings.Split(searchString, " ") - allWordsMatch := true - for _, word := range words { - wordTrimmed := strings.TrimSpace(word) - if !strings.Contains(strings.ToLower(decryptedText), strings.ToLower(wordTrimmed)) { - allWordsMatch = false - break - } - } - if allWordsMatch { - context := getContext(decryptedText, strings.TrimSpace(words[0]), false) - results = append(results, map[string]any{ - "year": year, - "month": month, - "day": day, - "text": context, - }) - } - } else { - // Simple search - if strings.Contains(strings.ToLower(decryptedText), strings.ToLower(searchString)) { - context := getContext(decryptedText, searchString, false) - results = append(results, map[string]any{ - "year": year, - "month": month, - "day": day, - "text": context, - }) - } - } - } - - // Check filenames - if files, ok := dayLog["files"].([]any); ok { - for _, fileInterface := range files { - file, ok := fileInterface.(map[string]any) - if !ok { - continue - } - - if encFilename, ok := file["enc_filename"].(string); ok { - decryptedFilename, err := utils.DecryptText(encFilename, encKey) - if err != nil { - continue - } - - if strings.Contains(strings.ToLower(decryptedFilename), strings.ToLower(searchString)) { - context := "📎 " + decryptedFilename - results = append(results, map[string]any{ - "year": year, - "month": month, - "day": day, - "text": context, - }) - break - } - } - } - } + // Decrypt text and date + decryptedText, err := utils.DecryptText(text, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting history text: %v", err), http.StatusInternalServerError) + return } - } - } - // Sort results by date - sort.Slice(results, func(i, j int) bool { - ri := results[i].(map[string]any) - rj := results[j].(map[string]any) - - yearI, _ := strconv.Atoi(ri["year"].(string)) - yearJ, _ := strconv.Atoi(rj["year"].(string)) - if yearI != yearJ { - return yearI < yearJ - } + decryptedDate, err := utils.DecryptText(dateWritten, encKey) + if err != nil { + http.Error(w, fmt.Sprintf("Error decrypting history date: %v", err), http.StatusInternalServerError) + return + } - monthI, _ := strconv.Atoi(ri["month"].(string)) - monthJ, _ := strconv.Atoi(rj["month"].(string)) - if monthI != monthJ { - return monthI < monthJ + result = append(result, map[string]any{ + "text": decryptedText, + "date_written": decryptedDate, + }) } - dayI := ri["day"].(int) - dayJ := rj["day"].(int) - return dayI < dayJ - }) + // Return history + utils.JSONResponse(w, http.StatusOK, result) + return + } - // Return results - utils.JSONResponse(w, http.StatusOK, results) + // Day not found + utils.JSONResponse(w, http.StatusOK, []any{}) } diff --git a/backend/handlers/users.go b/backend/handlers/users.go index d09239b..6cf551c 100644 --- a/backend/handlers/users.go +++ b/backend/handlers/users.go @@ -47,6 +47,7 @@ func Login(w http.ResponseWriter, r *http.Request) { var hashedPassword string var salt string found := false + var username string for _, u := range usersList { user, ok := u.(map[string]any) @@ -54,7 +55,7 @@ func Login(w http.ResponseWriter, r *http.Request) { continue } - if username, ok := user["username"].(string); ok && username == req.Username { + if username, ok = user["username"].(string); ok && strings.EqualFold(username, req.Username) { found = true if id, ok := user["user_id"].(float64); ok { userID = int(id) @@ -78,7 +79,7 @@ func Login(w http.ResponseWriter, r *http.Request) { return } - oldUsersList, ok := oldUsers["users"].([]interface{}) + oldUsersList, ok := oldUsers["users"].([]any) if !ok || len(oldUsersList) == 0 { utils.Logger.Printf("Login failed. User '%s' not found in new or old data", req.Username) http.Error(w, "User/Password combination not found", http.StatusNotFound) @@ -93,7 +94,7 @@ func Login(w http.ResponseWriter, r *http.Request) { continue } - if username, ok := user["username"].(string); ok && username == req.Username { + if username, ok = user["username"].(string); ok && strings.EqualFold(username, req.Username) { oldUser = user break } @@ -125,7 +126,7 @@ func Login(w http.ResponseWriter, r *http.Request) { // Check if there is already a migration in progress for this user activeMigrationsMutex.RLock() - isActive := activeMigrations[req.Username] + isActive := activeMigrations[username] activeMigrationsMutex.RUnlock() if isActive { @@ -138,7 +139,7 @@ func Login(w http.ResponseWriter, r *http.Request) { // Mark this user as having an active migration activeMigrationsMutex.Lock() - activeMigrations[req.Username] = true + activeMigrations[username] = true activeMigrationsMutex.Unlock() // Create a channel to report progress @@ -153,7 +154,7 @@ func Login(w http.ResponseWriter, r *http.Request) { for progress := range progressChan { migrationProgressMutex.Lock() // Convert from utils.MigrationProgress to handlers.MigrationProgress - migrationProgress[req.Username] = MigrationProgress{ + migrationProgress[username] = MigrationProgress{ Phase: progress.Phase, ProcessedItems: progress.ProcessedItems, TotalItems: progress.TotalItems, @@ -163,26 +164,28 @@ func Login(w http.ResponseWriter, r *http.Request) { } }() - err := utils.MigrateUserData(req.Username, req.Password, Register, progressChan) + utils.Logger.Printf("Starting migration for user '%s'", username) + + err := utils.MigrateUserData(username, req.Password, Register, progressChan) if err != nil { - utils.Logger.Printf("Migration failed for user '%s': %v", req.Username, err) + utils.Logger.Printf("Migration failed for user '%s': %v", username, err) // Mark migration as completed even on error activeMigrationsMutex.Lock() - activeMigrations[req.Username] = false + activeMigrations[username] = false activeMigrationsMutex.Unlock() return } // Mark migration as completed activeMigrationsMutex.Lock() - activeMigrations[req.Username] = false + activeMigrations[username] = false activeMigrationsMutex.Unlock() }() // Return migration status to client utils.JSONResponse(w, http.StatusAccepted, map[string]any{ "migration_started": true, - "username": req.Username, + "username": username, }) return } @@ -222,7 +225,7 @@ func Login(w http.ResponseWriter, r *http.Request) { // Return success utils.JSONResponse(w, http.StatusOK, map[string]any{ "migration_started": false, - "username": req.Username, + "username": username, }) } diff --git a/backend/main.go b/backend/main.go index 6ab09a3..238032e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -32,7 +32,6 @@ func main() { // Register routes mux.HandleFunc("POST /users/login", handlers.Login) - mux.HandleFunc("GET /users/migrationWebSocket", handlers.HandleMigrationWebSocket) mux.HandleFunc("GET /users/migrationProgress", handlers.GetMigrationProgress) mux.HandleFunc("GET /users/isRegistrationAllowed", handlers.IsRegistrationAllowed) mux.HandleFunc("POST /users/register", handlers.RegisterHandler) diff --git a/backend/utils/migration.go b/backend/utils/migration.go index 51093ad..685ff02 100644 --- a/backend/utils/migration.go +++ b/backend/utils/migration.go @@ -346,15 +346,11 @@ func MigrateUserData(username, password string, registerFunc RegisterUserFunc, p return handleError("Error getting encryption key", err) } - time.Sleep(800 * time.Millisecond) // Simulate some delay for migration - // Migrate templates if err := migrateTemplates(oldDataDir, newDataDir, oldEncKey, encKey, ¤tProgress, progressChan); err != nil { return handleError("Error migrating templates", err) } - time.Sleep(800 * time.Millisecond) // Simulate some delay for migration - // Migrate logs (years/months) if err := migrateLogs(oldDataDir, newDataDir, oldEncKey, encKey, ¤tProgress, progressChan); err != nil { return handleError("Error migrating logs", err) @@ -438,13 +434,91 @@ func migrateTemplates(oldDir, newDir string, oldKey string, newKey string, progr return fmt.Errorf("error reading old templates: %v", err) } - // Templates need to be parsed and re-written with proper indentation + // Templates need to be parsed var templatesData map[string]any if err := json.Unmarshal(oldTemplatesBytes, &templatesData); err != nil { return fmt.Errorf("error parsing old templates: %v", err) } - // Create templates.json file with proper indentation + // Decrypt and encrypt templates + oldKeyBytes, err := base64.URLEncoding.DecodeString(oldKey) + if err != nil { + Logger.Printf("Error decoding oldKey %v", err) + progress.ErrorCount++ + return fmt.Errorf("error decoding oldKey: %v", err) + } + + // Get the templates array + templatesArray, ok := templatesData["templates"].([]any) + if !ok { + return fmt.Errorf("invalid templates format - 'templates' is not an array") + } + + // Create a new templates array for the migrated templates + newTemplatesArray := make([]map[string]any, 0) + + // Process each template in the array + for _, templateItem := range templatesArray { + templateMap, ok := templateItem.(map[string]any) + if !ok { + Logger.Printf("Warning: template item is not a map: %v", templateItem) + progress.ErrorCount++ + continue + } + + // Create a new template without the 'number' field + newTemplate := make(map[string]any) + + // Process encrypted name + if encName, ok := templateMap["name"].(string); ok && encName != "" { + // Decrypt with old key + decryptedName, err := FernetDecrypt(encName, oldKeyBytes) + if err != nil { + Logger.Printf("Error decrypting template name: %v", err) + progress.ErrorCount++ + continue + } + + // Encrypt with new key + newEncName, err := EncryptText(decryptedName, newKey) + if err != nil { + Logger.Printf("Error encrypting template name: %v", err) + progress.ErrorCount++ + continue + } + + newTemplate["name"] = newEncName + } + + // Process encrypted text + if encText, ok := templateMap["text"].(string); ok && encText != "" { + // Decrypt with old key + decryptedText, err := FernetDecrypt(encText, oldKeyBytes) + if err != nil { + Logger.Printf("Error decrypting template text: %v", err) + progress.ErrorCount++ + continue + } + + // Encrypt with new key + newEncText, err := EncryptText(decryptedText, newKey) + if err != nil { + Logger.Printf("Error encrypting template text: %v", err) + progress.ErrorCount++ + continue + } + + newTemplate["text"] = newEncText + } + + // Add the new template to the array + newTemplatesArray = append(newTemplatesArray, newTemplate) + } + + // Replace the old templates array with the new one + templatesData["templates"] = newTemplatesArray + + // Create templates.json file newTemplatesPath := filepath.Join(newDir, "templates.json") templatesMutex.Lock() @@ -740,7 +814,6 @@ func migrateLogs(oldDir, newDir string, oldKey string, newKey string, progress * logsMutex.Unlock() processedMonths++ - time.Sleep(20 * time.Millisecond) // Simulate some delay for migration } // Final progress update @@ -1004,7 +1077,6 @@ func migrateFiles(oldFilesDir, newDir string, oldKey string, newKey string, prog progressChan <- *progress } } - time.Sleep(100 * time.Millisecond) // Simulate some delay for migration } // Third pass: update all month files with new file IDs diff --git a/frontend/src/lib/helpers.js b/frontend/src/lib/helpers.js index f2eb91e..bb8590b 100644 --- a/frontend/src/lib/helpers.js +++ b/frontend/src/lib/helpers.js @@ -25,4 +25,6 @@ export { formatBytes, sameDate }; export let alwaysShowSidenav = writable(true); // check if offcanvas/sidenav is open -export let offcanvasIsOpen = writable(false); \ No newline at end of file +export let offcanvasIsOpen = writable(false); + +export let currentUser = writable("") \ No newline at end of file diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index f42dfd2..beb7d3e 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -20,7 +20,7 @@ import trianglify from 'trianglify'; import { tags } from '$lib/tagStore.js'; import TagModal from '$lib/TagModal.svelte'; - import { alwaysShowSidenav } from '$lib/helpers.js'; + import { alwaysShowSidenav, currentUser } from '$lib/helpers.js'; import { templates } from '$lib/templateStore'; import { faRightFromBracket, @@ -75,11 +75,22 @@ } }); + $effect(() => { + if ($currentUser !== null) { + getUserSettings(); + getTemplates(); + } else { + $settings = {}; + $templates = []; + } + }); + function logout() { axios .get(API_URL + '/users/logout') .then((response) => { localStorage.removeItem('user'); + $currentUser = null; goto('/login'); }) .catch((error) => { @@ -403,7 +414,7 @@ } function updateSelectedTemplate() { - if (selectedTemplate === '-1' || selectedTemplate === null) { + if (selectedTemplate === '-1' || selectedTemplate === null || $templates.length === 0) { // new template templateName = ''; templateText = ''; @@ -860,8 +871,8 @@ {#if deleteTagId === tag.id}