Skip to content

Deploying BookLogr

BookLogr is a simple, self-hosted service designed to help you manage your personal book library with complete control over your data. Built with React frontend and Python Flask backend, BookLogr provides an elegant solution for tracking books you own, read, or want to read. Whether you’re an avid reader maintaining a personal catalog, a book collector organizing your collection, or someone wanting to share their reading journey with friends and family, BookLogr offers the privacy and flexibility of self-hosted infrastructure combined with powerful library management features.

Why BookLogr?

BookLogr stands out as the premier choice for self-hosted personal library management with exceptional features:

  • Self-Hosted Control: Complete control over your data with self-hosted infrastructure
  • Book Lookup: Easily search and add books by title or ISBN powered by OpenLibrary
  • Reading Lists: Organize books into predefined lists (Reading, Already Read, To Be Read)
  • Progress Tracking: Track your current page in books you’re actively reading
  • Book Ratings: Rate books on a 0.5 to 5-star scale
  • Notes & Quotes: Save short notes and meaningful quotes from books you read
  • Public Library Sharing: Optional public profile to share your library with friends and family
  • Mastodon Integration: Automatically share reading progress to Mastodon
  • Data Export: Export your complete library in multiple formats (CSV, JSON, HTML)
  • Database Flexibility: Choose between SQLite (default) or PostgreSQL
  • Multi-Language Support: Available in multiple languages including Simplified Chinese
  • Privacy First: Your reading data stays on your own server
  • Responsive Design: Works seamlessly on desktop and mobile devices
  • Open Source: Apache 2.0 licensed with active community development
  • Modern Tech Stack: React frontend with Python Flask backend
  • Book Metadata: Automatically fetch and display book information
  • Search Functionality: Full-text search through your personal library
  • Advanced Filtering: Filter by reading status, ratings, and dates
  • Reading Statistics: Track reading habits and book statistics
  • User Management: Support for multiple user accounts
  • API Available: RESTful API for integrations

BookLogr is ideal for personal book collectors managing their reading journey, avid readers tracking literary history, book clubs organizing shared reading, users prioritizing privacy over cloud services, and those wanting complete data ownership. With persistent storage on Klutch.sh, your personal library remains secure and always accessible.

Prerequisites

Before deploying BookLogr, ensure you have:

  • A Klutch.sh account
  • A GitHub repository with your BookLogr deployment configuration
  • Basic familiarity with Docker and Git
  • Sufficient storage for your book library metadata (typically 5-50GB depending on library size)
  • Database knowledge (SQLite or PostgreSQL)
  • Understanding of Python Flask applications
  • A custom domain for your personal library (recommended)

Important Considerations

Deploying BookLogr

  1. Create a New Project

    Log in to your Klutch.sh dashboard and create a new project for your BookLogr instance.

  2. Prepare Your Repository

    Create a GitHub repository with the following structure for your BookLogr deployment:

    booklogr-deploy/
    ├─ Dockerfile
    ├─ .env.example
    ├─ docker-entrypoint.sh
    ├─ .gitignore
    └─ README.md

    Here’s a Dockerfile for BookLogr:

    FROM node:18-alpine AS frontend-builder
    WORKDIR /app/web
    # Clone and build frontend
    RUN apk add --no-cache git && \
    git clone https://github.com/Mozzo1000/booklogr.git /tmp/booklogr && \
    cp -r /tmp/booklogr/web/* . && \
    npm ci && \
    npm run build
    FROM python:3.11-slim
    WORKDIR /app
    # Install system dependencies
    RUN apt-get update && apt-get install -y \
    curl \
    git \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*
    # Clone BookLogr repository
    RUN git clone https://github.com/Mozzo1000/booklogr.git /tmp/booklogr && \
    cp -r /tmp/booklogr/* .
    # Install Python dependencies
    RUN pip install --no-cache-dir poetry && \
    poetry config virtualenvs.create false && \
    poetry install --no-dev
    # Copy built frontend from builder
    COPY --from=frontend-builder /app/web/build ./web/build
    # Create necessary directories
    RUN mkdir -p /app/data \
    /app/logs \
    /app/instance && \
    chmod -R 755 /app
    # Copy entrypoint script
    COPY docker-entrypoint.sh /
    RUN chmod +x /docker-entrypoint.sh
    # Expose port
    EXPOSE 5000
    # Health check
    HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:5000/ || exit 1
    # Run entrypoint
    ENTRYPOINT ["/docker-entrypoint.sh"]
    CMD ["python", "-m", "flask", "run", "--host=0.0.0.0"]

    Create a docker-entrypoint.sh file:

    #!/bin/bash
    set -e
    # Create necessary directories
    mkdir -p /app/data \
    /app/logs \
    /app/instance
    # Set proper permissions
    chmod -R 755 /app
    echo "Initializing BookLogr..."
    # Wait for database (if using PostgreSQL)
    if [ "$DATABASE_URL" != "${DATABASE_URL#postgresql}" ]; then
    echo "Waiting for PostgreSQL..."
    until PGPASSWORD=$DATABASE_PASSWORD psql -h $DATABASE_HOST -U $DATABASE_USER -d postgres -c "\q" 2>/dev/null; do
    echo "PostgreSQL is unavailable - sleeping..."
    sleep 2
    done
    echo "PostgreSQL is up!"
    fi
    # Initialize database
    echo "Running database migrations..."
    python -m flask db upgrade || true
    # Create default user if specified
    if [ -n "$AUTH_DEFAULT_USER" ] && [ -n "$AUTH_DEFAULT_PASSWORD" ]; then
    echo "Setting up default user..."
    python << 'EOF'
    import os
    from app import create_app
    from app.models import User
    app = create_app()
    with app.app_context():
    user = User.query.filter_by(username=os.getenv('AUTH_DEFAULT_USER')).first()
    if not user:
    user = User(username=os.getenv('AUTH_DEFAULT_USER'))
    user.set_password(os.getenv('AUTH_DEFAULT_PASSWORD'))
    from app import db
    db.session.add(user)
    db.session.commit()
    print(f"Created user: {os.getenv('AUTH_DEFAULT_USER')}")
    else:
    print(f"User {os.getenv('AUTH_DEFAULT_USER')} already exists")
    EOF
    fi
    echo "BookLogr is starting..."
    exec "$@"

    Create a .env.example file:

    Terminal window
    # BookLogr Configuration
    FLASK_APP=app.py
    FLASK_ENV=production
    SECRET_KEY=change-me-to-a-secret-key-32-characters
    # Database Configuration (SQLite default)
    DATABASE_URL=sqlite:////app/data/booklogr.db
    # OR PostgreSQL Configuration
    # DATABASE_URL=postgresql://user:password@database.internal:5432/booklogr
    # DATABASE_PASSWORD=secure_password
    # Server Configuration
    SERVER_NAME=booklogr.yourdomain.com
    PREFERRED_URL_SCHEME=https
    # Authentication
    AUTH_DEFAULT_USER=admin
    AUTH_DEFAULT_PASSWORD=secure_password
    AUTH_TYPE=local
    # Application Settings
    TITLE=BookLogr
    LANGUAGE=en
    TIMEZONE=UTC
    # OpenLibrary API
    OPENLIBRARY_BASE_URL=https://openlibrary.org
    # Email Configuration (optional)
    MAIL_SERVER=smtp.gmail.com
    MAIL_PORT=587
    MAIL_USE_TLS=true
    MAIL_USERNAME=your-email@gmail.com
    MAIL_PASSWORD=app-password
    # Mastodon Integration (optional)
    MASTODON_ENABLED=false
    MASTODON_INSTANCE=mastodon.social
    MASTODON_ACCESS_TOKEN=your-access-token
    # Logging
    LOG_LEVEL=INFO
    LOG_FILE=/app/logs/booklogr.log
    # Features
    ALLOW_PUBLIC_PROFILE=true
    ALLOW_USER_REGISTRATION=false
    ALLOW_DATA_EXPORT=true

    Create a .gitignore file:

    .env
    __pycache__/
    *.pyc
    .venv/
    data/
    logs/
    instance/
    build/
    dist/
    *.egg-info/
    node_modules/
    .DS_Store
    *.db
    *.sqlite
    .idea/

    Commit and push to your GitHub repository:

    Terminal window
    git init
    git add .
    git commit -m "Initial BookLogr deployment"
    git remote add origin https://github.com/yourusername/booklogr-deploy.git
    git push -u origin main
  3. Create a New App

    In the Klutch.sh dashboard:

    • Click “Create New App”
    • Select your GitHub repository containing the Dockerfile
    • Choose the branch (typically main or master)
    • Klutch.sh will automatically detect the Dockerfile in the root directory
  4. Configure Environment Variables

    Set up these essential environment variables in your Klutch.sh dashboard:

    VariableDescriptionExample
    FLASK_APPFlask application entryapp.py
    FLASK_ENVEnvironment modeproduction
    SECRET_KEYSession encryption keyyour-secret-key-32-chars
    DATABASE_URLDatabase connection stringsqlite:////app/data/booklogr.db
    SERVER_NAMEYour domainbooklogr.yourdomain.com
    AUTH_DEFAULT_USERAdmin usernameadmin
    AUTH_DEFAULT_PASSWORDAdmin passwordsecure_password
    TITLEApplication titleBookLogr
    LANGUAGEDefault languageen
    TIMEZONEServer timezoneUTC
    ALLOW_PUBLIC_PROFILEPublic library sharingtrue
    ALLOW_DATA_EXPORTEnable data exporttrue
    LOG_LEVELLogging verbosityINFO
  5. Configure Persistent Storage

    BookLogr requires persistent storage for database and library data. Add persistent volumes:

    Mount PathDescriptionRecommended Size
    /app/dataSQLite database and application data50GB
    /app/logsApplication logs10GB
    /app/instanceInstance-specific files5GB

    In the Klutch.sh dashboard:

    • Navigate to your app settings
    • Go to the “Volumes” section
    • Click “Add Volume” for each mount path
    • Set mount paths and sizes as specified above
    • Ensure data volume is sized for your library size
  6. Set Network Configuration

    Configure your app’s network settings:

    • Select traffic type: HTTP
    • Recommended internal port: 5000 (Flask default development port)
    • Klutch.sh will handle HTTPS termination via reverse proxy
    • The application will be accessible at your configured domain
  7. Configure Custom Domain

    BookLogr works best with a custom domain for personal library access:

    • Navigate to your app’s “Domains” section in Klutch.sh
    • Click “Add Custom Domain”
    • Enter your domain (e.g., library.yourdomain.com or booklogr.yourdomain.com)
    • Configure DNS with a CNAME record pointing to your Klutch.sh app
    • Update SERVER_NAME environment variable to match your domain
    • Klutch.sh will automatically provision SSL certificates
    • Access your library at https://library.yourdomain.com
  8. Deploy Your App

    • Review all settings and environment variables carefully
    • Verify all persistent volumes are properly configured with adequate sizes
    • Ensure database connection string matches your configuration
    • Click “Deploy”
    • Klutch.sh will build the Docker image and start your BookLogr instance
    • Wait for the deployment to complete (typically 10-15 minutes)
    • Access your BookLogr instance at your configured domain
    • Log in with admin credentials to begin building your library

Initial Setup and Configuration

After deployment completes, access your BookLogr instance to set up your personal library.

Accessing BookLogr

Navigate to your domain: https://library.yourdomain.com

Log in with the admin credentials you configured during setup.

Dashboard Overview

Explore the main interface:

  1. My Library: View all books in your library
  2. Reading: Books currently being read
  3. Already Read: Completed books with ratings
  4. To Be Read: Books on your wishlist
  5. Settings: Configure application preferences
  6. Profile: Manage your account and public profile

Adding Books to Your Library

Build your library with ease:

  1. Click “Add Book” button
  2. Search by title, author, or ISBN
  3. Select book from search results
  4. Confirm book details (title, author, cover)
  5. Assign to reading list (Reading, Already Read, To Be Read)
  6. Click “Add to Library”

Alternatively, scan book ISBN with mobile device:

  1. Use mobile app or browser camera
  2. Scan book barcode
  3. Automatically adds book to library
  4. Assign reading list

Organizing Your Library

Manage your books effectively:

  1. Reading List Management:

    • Move books between lists by updating status
    • Track books you’re currently reading
    • Mark completed books
    • Create wishlist of books to read
  2. Book Details:

    • View book metadata (cover, synopsis, author)
    • Add personal ratings (0.5-5 stars)
    • Write notes about the book
    • Save meaningful quotes
    • Record reading progress (current page)
  3. Filtering and Search:

    • Search by title, author, or genre
    • Filter by reading status
    • Filter by rating
    • Sort by date added or title
    • Advanced search capabilities

Tracking Reading Progress

Monitor your reading journey:

  1. For each book, set current page number
  2. Update progress as you read
  3. System tracks reading start date
  4. View completion percentage
  5. Get reading statistics

Rating and Reviewing Books

Document your reading experience:

  1. Open book details
  2. Set rating from 0.5 to 5 stars
  3. Write personal notes about the book
  4. Save meaningful quotes from reading
  5. Add completion date
  6. View all ratings and notes

Managing Your Profile

Configure your public presence:

  1. Navigate to “Settings” → “Profile”
  2. Set profile visibility (private/public)
  3. Customize display name
  4. Configure public library sharing
  5. Set profile description
  6. Manage profile picture

Public Library Sharing

Share your reading journey:

  1. Enable “Allow Public Profile” in settings
  2. Configure public library visibility
  3. Choose what information to display:
    • Book list and ratings
    • Reading statistics
    • Currently reading
    • Completed books
  4. Get shareable link to your library
  5. Users can view without login

Configuring Email Settings

Set up email notifications (optional):

  1. Navigate to “Settings” → “Email”
  2. Configure SMTP server settings:
    • SMTP server address
    • SMTP port
    • Email authentication
  3. Set notification preferences
  4. Test email configuration
  5. Enable/disable notifications

Setting Up Mastodon Integration

Share reading progress automatically:

  1. Go to “Settings” → “Mastodon Integration”
  2. Enable Mastodon sharing
  3. Enter Mastodon instance URL
  4. Generate and paste access token
  5. Configure what to share:
    • Book completions
    • Reading progress
    • Book ratings
  6. Posts automatically share to Mastodon

Exporting Your Data

Backup or migrate your library:

  1. Navigate to “Settings” → “Data Export”
  2. Choose export format:
    • CSV: Spreadsheet format for spreadsheet applications
    • JSON: Complete data export for backups
    • HTML: Viewable library webpage
  3. Click “Export”
  4. Download exported file
  5. Store backup in safe location

Database Configuration

For PostgreSQL deployments:

  1. Update DATABASE_URL environment variable
  2. Format: postgresql://user:password@host:port/database
  3. Run database migrations
  4. Verify connection successful
  5. Monitor database performance

Environment Variable Examples

Basic SQLite Configuration

Terminal window
FLASK_APP=app.py
FLASK_ENV=production
SECRET_KEY=abc123def456ghi789jkl012mno345pqr
DATABASE_URL=sqlite:////app/data/booklogr.db
SERVER_NAME=library.yourdomain.com
AUTH_DEFAULT_USER=admin
AUTH_DEFAULT_PASSWORD=secure_password
ALLOW_PUBLIC_PROFILE=true

Complete PostgreSQL Production Configuration

Terminal window
# Application
FLASK_APP=app.py
FLASK_ENV=production
SECRET_KEY=very_secure_secret_key_32_characters_long
DEBUG=false
# Database - PostgreSQL
DATABASE_URL=postgresql://booklogr:secure_db_password@postgres.internal:5432/booklogr
DATABASE_PASSWORD=secure_db_password
DATABASE_USER=booklogr
DATABASE_HOST=postgres.internal
DATABASE_PORT=5432
DATABASE_NAME=booklogr
# Server Configuration
SERVER_NAME=library.yourdomain.com
PREFERRED_URL_SCHEME=https
WEB_CONCURRENCY=4
# Authentication
AUTH_DEFAULT_USER=admin
AUTH_DEFAULT_PASSWORD=very_secure_admin_password_32_chars
AUTH_TYPE=local
SESSION_TIMEOUT=2592000
# Application Settings
TITLE=My Personal Library
LANGUAGE=en
TIMEZONE=America/New_York
DEFAULT_PAGE_SIZE=20
# OpenLibrary Configuration
OPENLIBRARY_BASE_URL=https://openlibrary.org
OPENLIBRARY_TIMEOUT=10
CACHE_BOOK_DATA=true
CACHE_TTL=86400
# Email Configuration
MAIL_SERVER=smtp.gmail.com
MAIL_PORT=587
MAIL_USE_TLS=true
MAIL_USERNAME=noreply@yourdomain.com
MAIL_PASSWORD=app_specific_password
MAIL_DEFAULT_SENDER=BookLogr <noreply@yourdomain.com>
# Mastodon Integration
MASTODON_ENABLED=true
MASTODON_INSTANCE=mastodon.social
MASTODON_ACCESS_TOKEN=your_mastodon_access_token
MASTODON_POST_ON_COMPLETION=true
# Features
ALLOW_PUBLIC_PROFILE=true
ALLOW_USER_REGISTRATION=false
ALLOW_DATA_EXPORT=true
ALLOW_MULTIPLE_USERS=false
# Logging
LOG_LEVEL=INFO
LOG_FILE=/app/logs/booklogr.log
LOG_MAX_BYTES=10485760
LOG_BACKUP_COUNT=10
LOG_FORMAT=%(asctime)s - %(name)s - %(levelname)s - %(message)s
# Performance
SQLALCHEMY_ECHO=false
SQLALCHEMY_POOL_SIZE=10
SQLALCHEMY_POOL_RECYCLE=3600
SQLALCHEMY_TRACK_MODIFICATIONS=false
# Security
PREFERRED_URL_SCHEME=https
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTPONLY=true
SESSION_COOKIE_SAMESITE=Lax

Sample Code and Getting Started

Python - Book Management Integration

# BookLogr Book Management Integration
from app import create_app, db
from app.models import Book, User, Rating, Note
from sqlalchemy import func
from datetime import datetime
class BookLibrary:
def __init__(self, app=None):
self.app = app or create_app()
self.db = db
def add_book(self, user_id, book_data):
"""Add a book to user's library"""
book = Book(
user_id=user_id,
title=book_data.get('title'),
author=book_data.get('author'),
isbn=book_data.get('isbn'),
pages=book_data.get('pages'),
cover_url=book_data.get('cover_url'),
publication_date=book_data.get('publication_date'),
description=book_data.get('description'),
status=book_data.get('status', 'to_be_read'),
date_added=datetime.now()
)
self.db.session.add(book)
self.db.session.commit()
return book.id
def get_user_library(self, user_id, status=None):
"""Get all books in user's library, optionally filtered by status"""
query = Book.query.filter_by(user_id=user_id)
if status:
query = query.filter_by(status=status)
return query.order_by(Book.date_added.desc()).all()
def search_books(self, user_id, query_string):
"""Search user's library by title, author, or isbn"""
search_term = f"%{query_string}%"
results = Book.query.filter_by(user_id=user_id).filter(
(Book.title.ilike(search_term)) |
(Book.author.ilike(search_term)) |
(Book.isbn.ilike(search_term))
).all()
return results
def rate_book(self, user_id, book_id, rating_value):
"""Add or update book rating"""
if not 0.5 <= rating_value <= 5.0:
raise ValueError("Rating must be between 0.5 and 5.0")
rating = Rating.query.filter_by(
user_id=user_id,
book_id=book_id
).first()
if rating:
rating.value = rating_value
rating.date_rated = datetime.now()
else:
rating = Rating(
user_id=user_id,
book_id=book_id,
value=rating_value,
date_rated=datetime.now()
)
self.db.session.add(rating)
self.db.session.commit()
return rating
def add_note(self, user_id, book_id, note_text, page_number=None):
"""Add a note or quote to a book"""
note = Note(
user_id=user_id,
book_id=book_id,
text=note_text,
page_number=page_number,
date_created=datetime.now()
)
self.db.session.add(note)
self.db.session.commit()
return note.id
def update_reading_progress(self, user_id, book_id, current_page):
"""Update current page in book being read"""
book = Book.query.filter_by(
id=book_id,
user_id=user_id
).first()
if not book:
raise ValueError("Book not found")
book.current_page = current_page
book.last_read = datetime.now()
self.db.session.commit()
return {
'book_id': book.id,
'current_page': book.current_page,
'progress_percent': (current_page / book.pages) * 100 if book.pages else 0
}
def get_reading_statistics(self, user_id):
"""Get reading statistics for user"""
books = Book.query.filter_by(user_id=user_id).all()
total_books = len(books)
completed_books = len([b for b in books if b.status == 'read'])
reading_books = len([b for b in books if b.status == 'reading'])
to_read_books = len([b for b in books if b.status == 'to_be_read'])
avg_rating = self.db.session.query(func.avg(Rating.value)).filter(
Rating.user_id == user_id
).scalar() or 0
total_pages = sum([b.pages or 0 for b in completed_books])
return {
'total_books': total_books,
'completed_books': completed_books,
'currently_reading': reading_books,
'to_be_read': to_read_books,
'average_rating': round(avg_rating, 1),
'total_pages_read': total_pages
}
def export_library_json(self, user_id):
"""Export entire library as JSON"""
books = Book.query.filter_by(user_id=user_id).all()
export_data = {
'user_id': user_id,
'export_date': datetime.now().isoformat(),
'books': []
}
for book in books:
book_data = {
'id': book.id,
'title': book.title,
'author': book.author,
'isbn': book.isbn,
'pages': book.pages,
'status': book.status,
'date_added': book.date_added.isoformat()
}
# Add rating if exists
rating = Rating.query.filter_by(
user_id=user_id,
book_id=book.id
).first()
if rating:
book_data['rating'] = rating.value
# Add notes
notes = Note.query.filter_by(
user_id=user_id,
book_id=book.id
).all()
book_data['notes'] = [
{'text': n.text, 'page': n.page_number}
for n in notes
]
export_data['books'].append(book_data)
return export_data
# Usage example
with app.app_context():
library = BookLibrary()
# Add a book
book_id = library.add_book(1, {
'title': 'The Great Gatsby',
'author': 'F. Scott Fitzgerald',
'isbn': '978-0743273565',
'pages': 180,
'status': 'reading'
})
# Rate the book
library.rate_book(1, book_id, 4.5)
# Add notes
library.add_note(1, book_id, "Beautiful prose about the Jazz Age", page_number=45)
# Update reading progress
progress = library.update_reading_progress(1, book_id, 75)
# Get statistics
stats = library.get_reading_statistics(1)

JavaScript - Library Dashboard

// BookLogr Library Dashboard
class LibraryDashboard {
constructor(containerId, apiBaseUrl) {
this.container = document.getElementById(containerId);
this.apiBaseUrl = apiBaseUrl;
this.currentView = 'all';
this.books = [];
this.init();
}
async init() {
this.setupUI();
this.setupEventListeners();
await this.loadLibrary();
}
setupUI() {
this.container.innerHTML = `
<div class="library-dashboard">
<div class="library-header">
<h1>My Personal Library</h1>
<button id="addBookBtn" class="btn-primary">+ Add Book</button>
</div>
<div class="library-stats">
<div class="stat-card">
<div class="stat-number" id="totalBooks">0</div>
<div class="stat-label">Total Books</div>
</div>
<div class="stat-card">
<div class="stat-number" id="completedBooks">0</div>
<div class="stat-label">Completed</div>
</div>
<div class="stat-card">
<div class="stat-number" id="currentlyReading">0</div>
<div class="stat-label">Currently Reading</div>
</div>
<div class="stat-card">
<div class="stat-number" id="avgRating">0</div>
<div class="stat-label">Avg Rating</div>
</div>
</div>
<div class="library-filters">
<button class="filter-btn active" data-filter="all">All Books</button>
<button class="filter-btn" data-filter="reading">Reading</button>
<button class="filter-btn" data-filter="read">Already Read</button>
<button class="filter-btn" data-filter="to_be_read">To Be Read</button>
</div>
<div class="library-search">
<input type="text" id="searchInput" placeholder="Search by title, author, or ISBN..." />
</div>
<div id="bookGrid" class="book-grid"></div>
</div>
`;
this.bookGrid = document.getElementById('bookGrid');
}
setupEventListeners() {
document.getElementById('addBookBtn').addEventListener('click', () => {
this.openAddBookDialog();
});
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
this.currentView = e.target.dataset.filter;
this.displayBooks();
});
});
document.getElementById('searchInput').addEventListener('input', (e) => {
this.searchBooks(e.target.value);
});
}
async loadLibrary() {
try {
const response = await fetch(this.apiBaseUrl + '/api/library');
const data = await response.json();
this.books = data.books;
this.updateStats(data.statistics);
this.displayBooks();
} catch (error) {
console.error('Failed to load library:', error);
}
}
displayBooks() {
let filteredBooks = this.books;
if (this.currentView !== 'all') {
filteredBooks = this.books.filter(b => b.status === this.currentView);
}
if (filteredBooks.length === 0) {
this.bookGrid.innerHTML = '<p class="no-books">No books found</p>';
return;
}
this.bookGrid.innerHTML = filteredBooks.map(book => `
<div class="book-card" data-book-id="${book.id}">
<div class="book-cover">
${book.cover_url ? `<img src="${book.cover_url}" alt="${book.title}" />` : '<div class="no-cover">No Cover</div>'}
</div>
<div class="book-info">
<h3 class="book-title">${this.escapeHtml(book.title)}</h3>
<p class="book-author">by ${this.escapeHtml(book.author)}</p>
${book.rating ? `<div class="book-rating">${this.renderStars(book.rating)}</div>` : ''}
<div class="book-progress">
${book.status === 'reading' ? `
<div class="progress-bar">
<div class="progress-fill" style="width: ${(book.current_page / book.pages) * 100}%"></div>
</div>
<span class="progress-text">${book.current_page}/${book.pages} pages</span>
` : ''}
</div>
<div class="book-actions">
<button class="btn-small" onclick="dashboard.openBookDetails(${book.id})">Details</button>
<button class="btn-small" onclick="dashboard.removeBook(${book.id})">Remove</button>
</div>
</div>
</div>
`).join('');
}
updateStats(stats) {
document.getElementById('totalBooks').textContent = stats.total_books;
document.getElementById('completedBooks').textContent = stats.completed_books;
document.getElementById('currentlyReading').textContent = stats.currently_reading;
document.getElementById('avgRating').textContent = stats.average_rating.toFixed(1);
}
renderStars(rating) {
const fullStars = Math.floor(rating);
const hasHalf = rating % 1 !== 0;
let stars = ''.repeat(fullStars);
if (hasHalf) stars += '';
stars += ''.repeat(5 - fullStars - (hasHalf ? 1 : 0));
return stars;
}
searchBooks(query) {
if (!query) {
this.displayBooks();
return;
}
const filtered = this.books.filter(book =>
book.title.toLowerCase().includes(query.toLowerCase()) ||
book.author.toLowerCase().includes(query.toLowerCase()) ||
(book.isbn && book.isbn.includes(query))
);
this.bookGrid.innerHTML = filtered.map(book => `
<div class="book-card" data-book-id="${book.id}">
<div class="book-cover">
${book.cover_url ? `<img src="${book.cover_url}" alt="${book.title}" />` : '<div class="no-cover">No Cover</div>'}
</div>
<h3>${this.escapeHtml(book.title)}</h3>
<p>${this.escapeHtml(book.author)}</p>
</div>
`).join('');
}
openAddBookDialog() {
// Implementation for add book dialog
console.log('Opening add book dialog...');
}
async openBookDetails(bookId) {
// Implementation for book details
console.log('Opening book details for:', bookId);
}
async removeBook(bookId) {
if (confirm('Are you sure you want to remove this book?')) {
try {
await fetch(this.apiBaseUrl + '/api/books/' + bookId, {
method: 'DELETE'
});
await this.loadLibrary();
} catch (error) {
console.error('Failed to remove book:', error);
}
}
}
escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
}
// Initialize dashboard
const dashboard = new LibraryDashboard(
'libraryContainer',
'https://library.yourdomain.com'
);

Bash - Backup and Maintenance Script

#!/bin/bash
# BookLogr Backup and Maintenance Script
BACKUP_DIR="/backups/booklogr"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
DATABASE_PATH="/app/data/booklogr.db"
# Create backup directory
mkdir -p $BACKUP_DIR
echo "Starting BookLogr backup..."
# Backup SQLite database
if [ -f "$DATABASE_PATH" ]; then
echo "Backing up SQLite database..."
cp "$DATABASE_PATH" $BACKUP_DIR/booklogr_db_$TIMESTAMP.db
gzip $BACKUP_DIR/booklogr_db_$TIMESTAMP.db
fi
# Backup application data
echo "Backing up application data..."
tar -czf $BACKUP_DIR/booklogr_data_$TIMESTAMP.tar.gz \
/app/data \
/app/instance \
--exclude='/app/data/*.db-journal' \
2>/dev/null || true
# Backup logs
echo "Backing up logs..."
tar -czf $BACKUP_DIR/booklogr_logs_$TIMESTAMP.tar.gz \
/app/logs 2>/dev/null || true
# Cleanup old backups
echo "Cleaning up old backups..."
find $BACKUP_DIR -name "booklogr_*" -mtime +$RETENTION_DAYS -delete
# Verify backups
echo "Verifying backups..."
for file in $BACKUP_DIR/*_$TIMESTAMP.*; do
if [ -f "$file" ]; then
size=$(du -h "$file" | awk '{print $1}')
echo "✓ Created: $(basename $file) ($size)"
fi
done
# Calculate total backup size
TOTAL_SIZE=$(du -sh $BACKUP_DIR | awk '{print $1}')
echo ""
echo "Backup completed at: $TIMESTAMP"
echo "Total backup size: $TOTAL_SIZE"
echo "Backup location: $BACKUP_DIR"
# Database optimization (for SQLite)
if [ -f "$DATABASE_PATH" ]; then
echo ""
echo "Optimizing SQLite database..."
sqlite3 "$DATABASE_PATH" "VACUUM;"
sqlite3 "$DATABASE_PATH" "ANALYZE;"
fi
# Optional: Upload to cloud storage
# aws s3 sync $BACKUP_DIR s3://your-bucket/booklogr-backups/ \
# --exclude "*" --include "booklogr_*_$TIMESTAMP.*" \
# --delete
# Optional: Send backup notification
# curl -X POST https://hooks.slack.com/services/YOUR/WEBHOOK/URL \
# -d "{\"text\":\"BookLogr backup completed: $(du -sh $BACKUP_DIR | awk '{print $1}')\"}"

cURL - API Integration Examples

Terminal window
# BookLogr API Examples
# Get all books in library
curl -X GET https://library.yourdomain.com/api/library \
-H "Content-Type: application/json"
# Get books by status
curl -X GET "https://library.yourdomain.com/api/library?status=reading" \
-H "Content-Type: application/json"
# Search books
curl -X GET "https://library.yourdomain.com/api/library/search?query=great+gatsby" \
-H "Content-Type: application/json"
# Get book details
curl -X GET https://library.yourdomain.com/api/books/123 \
-H "Content-Type: application/json"
# Add book to library
curl -X POST https://library.yourdomain.com/api/books \
-H "Content-Type: application/json" \
-d '{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"isbn": "978-0743273565",
"pages": 180,
"status": "reading"
}'
# Rate a book
curl -X POST https://library.yourdomain.com/api/books/123/rating \
-H "Content-Type: application/json" \
-d '{
"rating": 4.5
}'
# Add note to book
curl -X POST https://library.yourdomain.com/api/books/123/notes \
-H "Content-Type: application/json" \
-d '{
"text": "Beautiful prose about the Jazz Age",
"page_number": 45
}'
# Update reading progress
curl -X PUT https://library.yourdomain.com/api/books/123/progress \
-H "Content-Type: application/json" \
-d '{
"current_page": 85
}'
# Get reading statistics
curl -X GET https://library.yourdomain.com/api/statistics \
-H "Content-Type: application/json"
# Export library
curl -X GET "https://library.yourdomain.com/api/export?format=json" \
-H "Content-Type: application/json" \
-o library_backup.json
# Get public profile
curl -X GET https://library.yourdomain.com/api/profile/public \
-H "Content-Type: application/json"
# Delete book from library
curl -X DELETE https://library.yourdomain.com/api/books/123 \
-H "Content-Type: application/json"

Library Management

Building Your Book Collection

Effective library organization:

  1. Regular Addition:

    • Add books as you acquire them
    • Use ISBN scan for quick addition
    • Import from wish lists
    • Manually add rare or hard-to-find books
  2. Reading Organization:

    • Categorize by reading status
    • Set reading goals
    • Track multiple books simultaneously
    • Maintain wishlist for future reading
  3. Library Curation:

    • Remove completed reads to archive
    • Update reading progress regularly
    • Rate completed books
    • Document meaningful passages

Sharing Your Library

Share your reading journey:

  1. Public Profile:

    • Enable public library sharing
    • Share unique profile link
    • Friends can view your books
    • No account required for viewers
  2. Social Sharing:

    • Share individual books
    • Post reading achievements
    • Share favorite quotes
    • Mastodon integration for broader sharing
  3. Data Privacy:

    • Control what’s publicly visible
    • Keep personal notes private
    • Manage access levels
    • Archive sensitive entries

Troubleshooting

Common Issues and Solutions

Issue: Cannot add books

Solutions:

  • Verify OpenLibrary API is accessible
  • Check ISBN format validity
  • Try searching by title instead of ISBN
  • Verify internet connection
  • Check API rate limiting

Issue: Reading progress not saving

Troubleshooting:

  • Verify database connectivity
  • Check disk space availability
  • Clear browser cache
  • Restart application
  • Review error logs

Issue: Slow library loading

Solutions:

  • Optimize database (VACUUM, ANALYZE)
  • Clear old logs
  • Reduce library size
  • Enable database indexing
  • Check server resources

Issue: Export not working

Troubleshooting:

  • Verify sufficient disk space
  • Check database integrity
  • Try different export format
  • Review application logs
  • Test file permissions

Issue: Mastodon integration failing

Solutions:

  • Verify Mastodon instance URL
  • Check access token validity
  • Ensure network connectivity
  • Review Mastodon API limits
  • Check application settings

Updating BookLogr

To update BookLogr to a newer version:

  1. Backup your database and data files
  2. Update your Dockerfile to latest version
  3. Commit and push to GitHub
  4. Klutch.sh will automatically rebuild
  5. Test all functionality after update
  6. Verify library data integrity
  7. Monitor logs for any issues

Use Cases

Personal Book Tracking

  • Track reading throughout the year
  • Maintain reading statistics
  • Rate and review books
  • Share favorite reads with friends

Book Club Management

  • Organize club book selections
  • Track member progress
  • Share notes and discussions
  • Plan club meetings

Reading Goals

  • Set annual reading targets
  • Monitor reading progress
  • Achieve reading milestones
  • Build reading habits

Rare Book Collection

  • Catalog valuable collection
  • Document book conditions
  • Share collection digitally
  • Preserve collection history

Family Library

  • Organize household books
  • Track borrowed books
  • Maintain reading history
  • Share collection with family

Additional Resources

Conclusion

Deploying BookLogr on Klutch.sh provides you with a powerful, self-hosted service to manage your personal book library with complete data control. With easy book lookup, reading progress tracking, rating and reviewing capabilities, public library sharing, data export options, and seamless Mastodon integration, BookLogr enables you to curate and share your reading journey. Klutch.sh’s managed infrastructure ensures your library is always available, secure, and performant, allowing you to focus on enjoying books and building your literary collection.

Start managing your personal library today by deploying BookLogr on Klutch.sh and experience the freedom of owning your reading data.