Skip to content

feat: Koreader plugin#2298

Closed
WorldTeacher wants to merge 66 commits intobooklore-app:developfrom
WorldTeacher:koreader-plugin
Closed

feat: Koreader plugin#2298
WorldTeacher wants to merge 66 commits intobooklore-app:developfrom
WorldTeacher:koreader-plugin

Conversation

@WorldTeacher
Copy link
Copy Markdown
Contributor

🚀 Pull Request

📝 Description

This PR adds the koreader plugin to record and synchronize reading sessions to booklore

🛠️ Changes Implemented

  • added a new API endpoint by-hash to get the bookID based on the calculated hash
  • updated KoreaderAuth to map the koreader user with the booklore account to sync sessions
  • added new tests for the by-hash method

🧪 Testing Strategy

I created a docker image and tested the plugin with these conditions:

  • connected
  • not connected

and monitored the behavior of the plugin, as well as booklore when sessions were being synced

📸 Visual Changes (if applicable)


⚠️ Required Pre-Submission Checklist

Please Read - This Checklist is Mandatory

Important Notice: We've experienced several production bugs recently due to incomplete pre-submission checks. To maintain code quality and prevent issues from reaching production, we're enforcing stricter adherence to this checklist.

All checkboxes below must be completed before requesting review. PRs that haven't completed these requirements will be sent back for completion.

Mandatory Requirements (please check ALL boxes):

  • Code adheres to project style guidelines and conventions
  • Branch synchronized with latest develop branch (please resolve any merge conflicts)
  • 🚨 CRITICAL: Automated unit tests added/updated to cover changes (MANDATORY for ALL Spring Boot backend and Angular frontend changes - this is non-negotiable)
  • 🚨 CRITICAL: All tests pass locally (run ./gradlew test for Spring Boot backend, and ng test for Angular frontend - NO EXCEPTIONS)
  • 🚨 CRITICAL: Manual testing completed in local development environment (verify your changes work AND no existing functionality is broken - test related features thoroughly)
  • Flyway migration versioning follows correct sequence (if database schema was modified)
  • Documentation PR submitted to booklore-docs (required for features or enhancements that introduce user-facing or visual changes) -> here

Why This Matters:

Recent production incidents have been traced back to:

  • Incomplete testing coverage (especially backend)
  • Merge conflicts not resolved before merge
  • Missing documentation for new features

Backend changes without tests will not be accepted. By completing this checklist thoroughly, you're helping maintain the quality and stability of Booklore for all users.

Note to Reviewers: Please verify the checklist is complete before beginning your review. If items are unchecked, kindly ask the contributor to complete them first.


💬 Additional Context (optional)

@WorldTeacher WorldTeacher marked this pull request as draft January 23, 2026 00:18
@WorldTeacher
Copy link
Copy Markdown
Contributor Author

WorldTeacher commented Jan 23, 2026

converted to draft, as the current implementation messes with the progress sync to/from koreader. A fix will be added soon, latest eta is 24.1 28.1

WorldTeacher and others added 7 commits January 23, 2026 17:12
rework settings ui
rework calculation of historical sessions to use page count provided by
koreader to calculate percentages
fix percentage being sent to be xx.x instead of 0.x
fix sqlite error by using sqlite by koreader
add file logging
add warning and confirmation to historical sync
update cache to store paths
reduce hash calculations by using cache
allow user to set decimals for session percentage
set minimum session duration
reworked network detection
extract settings from main file
add options to sync on suspend, connect to wifi on suspend
add option to only manually sync
add help texts for various actions
rework network handling
allow session stored messages to be silent
@WorldTeacher WorldTeacher marked this pull request as ready for review January 27, 2026 17:06
@WorldTeacher
Copy link
Copy Markdown
Contributor Author

@acx10

I think this is ready for review now and has already been tested by some users and myself

Feedback is appreciated, and changes can be made

@WorldTeacher WorldTeacher marked this pull request as ready for review March 8, 2026 21:43
@WorldTeacher
Copy link
Copy Markdown
Contributor Author

@acx10 ready here as well. Time to get this merged, it has been open for way too long

return book;
}

public Book getBookByHash(String md5Hash, boolean withDescription) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't check for admin access. Every other method in this file does user.getPermissions().isAdmin() before the library-scoped check. An admin who isn't explicitly assigned to the library will get a 404 here.


@Bean
@Order(3)
public SecurityFilterChain readingSessionsSecurityChain(HttpSecurity http, KoreaderAuthFilter koreaderAuthFilter) throws Exception {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new chain at @order(3) matches all /api/v1/reading-sessions/** requests, which means existing web UI reading session calls now go through this chain instead of the general JWT chain at @order(10). The KOReader filter silently skips when headers are missing so it probably works, but those endpoints are now pulled out of the general chain entirely. Any future security changes to the general chain won't apply to reading sessions anymore. Could this be scoped more narrowly, like just the /batch endpoint?

@NotNull(message = "Book type is required")
private BookFileType bookType;

@NotEmpty(message = "Sessions list cannot be empty")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no upper bound on the sessions list size. @notempty just means "at least 1". A client could send a million sessions in one request. Needs a @SiZe(max = ...) here.

BookLoreUser user = authenticationService.getAuthenticatedUser();
boolean isAdmin = user.getPermissions().isAdmin();

List<BookEntity> allBooks = isAdmin
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loads every book with metadata into memory for fuzzy matching. For someone with 10k+ books that's going to be rough. ISBN matching could easily be an exact DB query, only falling back to in-memory for title fuzzy search.

// Validate all session times
for (ReadingSessionItemRequest sessionItem : request.getSessions()) {
if (sessionItem.getEndTime().isBefore(sessionItem.getStartTime())) {
throw new IllegalArgumentException("End time must be after start time");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raw IllegalArgumentException here, should be ApiError.GENERIC_BAD_REQUEST.createException(...). Same thing on line 97 with UsernameNotFoundException. These might not get caught by the global error handler properly.

}

@Deprecated
public List<org.booklore.model.dto.response.BookSearchResult> fuzzySearchByTitle(String searchTitle) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is brand new and already @deprecated. If nothing calls it, just drop it.

Comment thread tools/README.md Outdated
- Books must exist in your Booklore library (matched by MD5 hash)
- Network connectivity to your Booklore server

Further documentation can be found over in the [docs](link to docs here)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[docs](link to docs here) is still a placeholder.


// Only log and process if KOReader headers are present
if (username != null && key != null) {
log.info("KoreaderAuthFilter: Processing KOReader auth for user: {}", username);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be noisy. Better remove it.

user.isSyncWithBookloreReader(),
bookLoreUserId,
List.of(new SimpleGrantedAuthority("ROLE_USER"))
log.info("KoreaderAuthFilter: Authentication successful for user: {}", username);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be noisy. Better remove it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completely out of scope for this probably, but would something like this be better behind a debug setting or env variable?

@@ -0,0 +1,14 @@
package org.booklore.model.dto.response;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file does not seem to be used anywhere.

@acx10 acx10 added the feature Addition of new functionality label Mar 11, 2026
@WorldTeacher
Copy link
Copy Markdown
Contributor Author

@acx10
addressed all review comments, removed the plugin folder

frontend and backend tests pass, docker image builds

@WorldTeacher
Copy link
Copy Markdown
Contributor Author

Closed due to differences in opinion of open source and how OSS should be handled

@davidbaranek
Copy link
Copy Markdown

That's a shame. I was looking forward to this feature for so long. Any chance this would be reopened in the feature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Addition of new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants