refactor: restructure project from bookmann to shelf

- Rename project from 'bookmann' to 'shelf'
- Move all backend/ contents to root level (flatten structure)
- Update Go module name from 'bookmann' to 'shelf'
- Update all import paths to use new 'shelf' module
- Update Dockerfile to work without backend/ subdirectory
- Update docker-compose.yml to use new structure and rename containers
- Update .gitignore for new file paths
- Update README.md with new project name and structure
- Regenerate database code with new module imports
This commit is contained in:
2026-01-23 09:08:04 -05:00
parent 152ed27200
commit 4318f8624b
31 changed files with 38 additions and 175 deletions
+2 -3
View File
@@ -197,9 +197,8 @@ coverage/
bruno/collection.bru bruno/collection.bru
# Generated static assets (SvelteKit build output) # Generated static assets (SvelteKit build output)
backend/cmd/server/static/ cmd/server/static/
# Built binaries # Built binaries
main main
backend/main server
backend/server
+7 -10
View File
@@ -6,20 +6,17 @@ WORKDIR /app
# Install sqlc # Install sqlc
RUN go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest RUN go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
# Copy backend source code # Copy source code
COPY backend/ ./backend/ COPY . .
# Download dependencies # Download dependencies
RUN cd backend && go mod tidy RUN go mod tidy
# Generate sqlc code # Generate sqlc code
RUN cd backend && sqlc generate RUN sqlc generate
# Copy templates (updated for new homepage)
COPY backend/templates ./backend/templates
# Build the application # Build the application
RUN cd backend && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ../main ./cmd/server RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/server
# Final stage # Final stage
FROM alpine:latest FROM alpine:latest
@@ -31,10 +28,10 @@ WORKDIR /root/
COPY --from=builder /app/main . COPY --from=builder /app/main .
# Copy migrations (if needed for initialization) # Copy migrations (if needed for initialization)
COPY --from=builder /app/backend/migrations ./migrations COPY --from=builder /app/migrations ./migrations
# Copy templates # Copy templates
COPY --from=builder /app/backend/templates ./templates COPY --from=builder /app/templates ./templates
# Expose port # Expose port
EXPOSE 8765 EXPOSE 8765
+18 -17
View File
@@ -1,6 +1,6 @@
# 📚 Bookmann # 📚 Shelf
A self-hosted ebook management system built with Go, PostgreSQL, HTMX, and Tailwind CSS (fully integrated into a single service) featuring multiple beautiful dark themes with Tokyo Night as default. A self-hosted ebook management system built with Go, PostgreSQL, HTMX, and Tailwind CSS featuring multiple beautiful dark themes with Tokyo Night as default. Smart folder scanning with real-time monitoring and rich EPUB metadata extraction.
## ✨ Features ## ✨ Features
@@ -35,7 +35,7 @@ A self-hosted ebook management system built with Go, PostgreSQL, HTMX, and Tailw
docker-compose up --build docker-compose up --build
``` ```
3. Access the application at http://localhost:8765 3. Access Shelf at http://localhost:8765
### Database ### Database
@@ -46,16 +46,15 @@ PostgreSQL runs on port 5432 with default credentials:
## Development ## Development
### Backend ### Development
```bash ```bash
cd backend
go mod tidy go mod tidy
go run github.com/sqlc-dev/sqlc/cmd/sqlc@latest generate go run github.com/sqlc-dev/sqlc/cmd/sqlc@latest generate
go run cmd/server/main.go go run cmd/server/main.go
``` ```
The application uses Go HTML templates for server-side rendering with HTMX for dynamic interactions. Templates are located in `backend/templates/`. The application uses Go HTML templates for server-side rendering with HTMX for dynamic interactions. Templates are located in `templates/`.
**✨ Enhanced Features:** **✨ Enhanced Features:**
- **Authentication**: Login/Register forms with multiple theme support at `/login` and `/register` - **Authentication**: Login/Register forms with multiple theme support at `/login` and `/register`
@@ -140,18 +139,20 @@ Use the included Bruno collection in the `bruno/` directory for testing the API:
``` ```
. .
├── backend/ ├── cmd/server/ # Application entry point
│ ├── cmd/server/ # Application entry point ├── internal/
│ ├── internal/ │ ├── config/ # Configuration management
│ ├── config/ # Configuration management │ ├── database/ # Database connection and queries
│ ├── database/ # Database connection and queries │ ├── handlers/ # HTTP handlers (auth + ebooks)
│ ├── handlers/ # HTTP handlers (auth + ebooks) └── services/ # Business logic services (ebook scanner)
│ │ └── services/ # Business logic services (ebook scanner) ├── migrations/ # Database migrations
│ ├── migrations/ # Database migrations ├── templates/ # Go HTML templates with HTMX
│ ├── templates/ # Go HTML templates with HTMX
│ └── sqlc.yaml # sqlc configuration
├── bruno/ # Bruno API testing collection ├── bruno/ # Bruno API testing collection
├── docker-compose.yml ├── Dockerfile # Docker build configuration
├── docker-compose.yml # Docker Compose setup
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── sqlc.yaml # SQL code generation config
└── README.md └── README.md
``` ```
-134
View File
@@ -1,134 +0,0 @@
package main
import (
"bookmann/internal/config"
"bookmann/internal/database"
"bookmann/internal/handlers"
"html/template"
"io"
"log"
"net/http"
"strings"
"github.com/go-playground/validator/v10"
jwtgo "github.com/golang-jwt/jwt"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type TemplateRenderer struct {
templates *template.Template
}
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
// CustomValidator wraps the go-playground validator
type CustomValidator struct {
validator *validator.Validate
}
func (cv *CustomValidator) Validate(i interface{}) error {
return cv.validator.Struct(i)
}
func main() {
cfg := config.LoadConfig()
pool, err := database.NewConnection(cfg.DatabaseURL())
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
defer pool.Close()
queries := database.New(pool)
e := echo.New()
// Set up validator
e.Validator = &CustomValidator{validator: validator.New()}
// Set up templates
renderer := &TemplateRenderer{
templates: template.Must(template.ParseGlob("templates/*.html")),
}
e.Renderer = renderer
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
// Auth routes (no auth required)
auth := handlers.NewAuthHandler(queries, cfg.JWTSecret)
e.POST("/api/auth/register", auth.Register)
e.POST("/api/auth/login", auth.Login)
// JWT middleware for protected routes
jwtMiddleware := middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: []byte(cfg.JWTSecret),
ContextKey: "user",
SuccessHandler: func(c echo.Context) {
token := c.Get("user").(*jwtgo.Token)
claims := token.Claims.(jwtgo.MapClaims)
c.Set("user_id", claims["user_id"])
},
})
// Protected routes
protected := e.Group("/api", jwtMiddleware)
protected.GET("/auth/profile", auth.GetProfile)
protected.POST("/auth/ebook-folders", auth.AddEbookFolder)
protected.GET("/auth/ebook-folders", auth.GetEbookFolders)
protected.DELETE("/auth/ebook-folders/:folderPath", auth.DeleteEbookFolder)
protected.GET("/users", auth.ListUsers)
// Routes
handlers.SetupRoutes(protected, queries)
// Static files
e.Static("/static", "static")
// Page routes
e.GET("/", func(c echo.Context) error {
// Check if user has valid JWT token
tokenString := c.Request().Header.Get("Authorization")
if tokenString != "" && strings.HasPrefix(tokenString, "Bearer ") {
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
token, err := jwtgo.Parse(tokenString, func(token *jwtgo.Token) (interface{}, error) {
return []byte(cfg.JWTSecret), nil
})
if err == nil && token.Valid {
// User is authenticated, get user info and serve dashboard
claims := token.Claims.(jwtgo.MapClaims)
userID := claims["user_id"].(string)
user, err := queries.GetUser(c.Request().Context(), pgtype.UUID{Bytes: uuid.MustParse(userID), Valid: true})
if err == nil {
return c.Render(http.StatusOK, "dashboard.html", map[string]interface{}{
"User": map[string]string{
"ID": uuid.UUID(user.ID.Bytes).String(),
"Username": user.Username,
"Email": user.Email,
},
})
}
}
}
// User not authenticated or token invalid, serve landing page
return c.Render(http.StatusOK, "index.html", nil)
})
e.GET("/login", func(c echo.Context) error {
return c.Render(http.StatusOK, "login.html", nil)
})
e.GET("/register", func(c echo.Context) error {
return c.Render(http.StatusOK, "register.html", nil)
})
// Start server
log.Printf("Starting server on port %s", cfg.ServerPort)
e.Logger.Fatal(e.Start(":" + cfg.ServerPort))
}
+6 -6
View File
@@ -3,14 +3,14 @@ version: "3.8"
services: services:
db: db:
image: postgres:15-alpine image: postgres:15-alpine
container_name: bookmann_db container_name: shelf_db
environment: environment:
POSTGRES_DB: ebookdb POSTGRES_DB: ebookdb
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DBPASS} POSTGRES_PASSWORD: ${DBPASS}
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
- ./backend/migrations:/docker-entrypoint-initdb.d - ./migrations:/docker-entrypoint-initdb.d
ports: ports:
- "5432:5432" - "5432:5432"
healthcheck: healthcheck:
@@ -21,11 +21,11 @@ services:
env_file: env_file:
- .env - .env
backend: app:
build: build:
context: . context: .
dockerfile: ./backend/Dockerfile dockerfile: ./Dockerfile
container_name: bookmann container_name: shelf
environment: environment:
DATABASE_HOST: db DATABASE_HOST: db
DATABASE_PORT: 5432 DATABASE_PORT: 5432
@@ -40,7 +40,7 @@ services:
db: db:
condition: service_healthy condition: service_healthy
volumes: volumes:
- ./backend/uploads:/app/uploads - ./uploads:/app/uploads
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8765/"] test: ["CMD", "curl", "-f", "http://localhost:8765/"]
interval: 30s interval: 30s
+1 -1
View File
@@ -1,4 +1,4 @@
module bookmann module shelf
go 1.25 go 1.25
View File
@@ -1,7 +1,7 @@
package handlers package handlers
import ( import (
"bookmann/internal/database" "shelf/internal/database"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
@@ -1,8 +1,8 @@
package handlers package handlers
import ( import (
"bookmann/internal/database" "shelf/internal/database"
"bookmann/internal/services" "shelf/internal/services"
"context" "context"
"net/http" "net/http"
"strconv" "strconv"
@@ -1,7 +1,7 @@
package services package services
import ( import (
"bookmann/internal/database" "shelf/internal/database"
"context" "context"
"fmt" "fmt"
"io/fs" "io/fs"
View File

Before

Width:  |  Height:  |  Size: 661 B

After

Width:  |  Height:  |  Size: 661 B