refactor: reorganize project structure and update configurations

- Move migrations/ to database/schema/ for clarity on database schema definitions
- Move sqlc.yaml to internal/database/ to group with database code
- Move static/ to cmd/server/static/ to co-locate with server
- Update all configuration files and documentation
- Follow Go project conventions for better organization
This commit is contained in:
2026-01-24 23:40:31 -05:00
parent 8a951fb242
commit 08e80ae84b
34 changed files with 532 additions and 104 deletions
+52 -3
View File
@@ -4,11 +4,14 @@ import (
"bookmann/internal/database"
"fmt"
"net/http"
"path/filepath"
"strings"
"time"
jwt "github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt"
@@ -370,6 +373,31 @@ type AddEbookFolderRequest struct {
FolderPath string `json:"folder_path" validate:"required"`
}
// normalizePath cleans and normalizes folder paths for consistent storage and comparison
func normalizePath(path string) string {
fmt.Printf("normalizePath input: '%s'\n", path)
var cleaned string
// Handle home directory expansion (~)
if strings.HasPrefix(path, "~/") {
// Keep the original path for ~ to preserve user's formatting
// Just normalize separators and that's it
cleaned = strings.ReplaceAll(path, "\\", "/")
} else {
// Clean the path to remove redundant separators, ., .. etc.
cleaned = filepath.Clean(path)
// Convert to consistent path separators (use forward slashes for storage)
cleaned = strings.ReplaceAll(cleaned, "\\", "/")
// Remove trailing slash unless it's root path
if len(cleaned) > 1 && strings.HasSuffix(cleaned, "/") {
cleaned = strings.TrimSuffix(cleaned, "/")
}
}
fmt.Printf("normalizePath output: '%s'\n", cleaned)
return cleaned
}
type EbookFolderResponse struct {
ID string `json:"id"`
UserID string `json:"user_id"`
@@ -397,9 +425,12 @@ func (h *AuthHandler) AddEbookFolder(c echo.Context) error {
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
// Normalize the folder path before storing
normalizedPath := normalizePath(req.FolderPath)
folder, err := h.db.AddUserEbookFolder(c.Request().Context(), database.AddUserEbookFolderParams{
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
FolderPath: req.FolderPath,
FolderPath: normalizedPath,
})
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
@@ -426,6 +457,12 @@ func (h *AuthHandler) GetEbookFolders(c echo.Context) error {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
fmt.Printf("GetEbookFolders: user_id='%s'\n", userID)
for _, folder := range folders {
fmt.Printf(" Folder in DB: id='%s', path='%s'\n",
uuid.UUID(folder.ID.Bytes).String(), folder.FolderPath)
}
var response []EbookFolderResponse
for _, folder := range folders {
response = append(response, EbookFolderResponse{
@@ -455,14 +492,26 @@ func (h *AuthHandler) DeleteEbookFolder(c echo.Context) error {
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
err = h.db.DeleteUserEbookFolder(c.Request().Context(), database.DeleteUserEbookFolderParams{
// Normalize the folder path before deletion
normalizedPath := normalizePath(req.FolderPath)
// Debug logging - remove in production
fmt.Printf("DeleteEbookFolder: original path='%s', normalized path='%s', user_id='%s'\n",
req.FolderPath, normalizedPath, userID)
deletedFolder, err := h.db.DeleteUserEbookFolder(c.Request().Context(), database.DeleteUserEbookFolderParams{
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
FolderPath: req.FolderPath,
FolderPath: normalizedPath,
})
if err != nil {
fmt.Printf("DeleteEbookFolder failed: %v\n", err)
if err == pgx.ErrNoRows {
return c.JSON(http.StatusNotFound, map[string]string{"error": "ebook folder not found"})
}
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
fmt.Printf("DeleteEbookFolder succeeded: deleted folder with path '%s'\n", deletedFolder.FolderPath)
return c.JSON(http.StatusOK, map[string]string{"message": "ebook folder deleted successfully"})
}