Skip to content

KoBup 2.0: bug fixes, security, merge restore, TypeScript UI, EN/IT, SEO prerender, PWA - #4

Merged
Fanfulla merged 8 commits into
mainfrom
improvements/2026-audit
Sep 27, 2026
Merged

Fanfulla merged 8 commits into
mainfrom
improvements/2026-audit

Conversation

@Fanfulla

Copy link
Copy Markdown
Owner

Summary

Full audit and modernization of KoBup, one commit per phase:

  • Cleanup: dead code, unused dependencies, real backups moved to git-ignored fixtures/
  • Toolchain: React 19, Vite 8, Vitest 5, Tailwind 4, Zustand 5, ESLint 10, strict TypeScript, CI
  • Bug fixes: reading time shown 60x too high (Kobo stores seconds), journal_mode typo, schema version from DbVersion, real model/firmware from .kobo/version, WAL warning, restore preview stats
  • Security: optional AES-256 backups, pre-restore safety copy with undo, hidden-path guard, strict CSP, self-hosted fonts
  • Features: merge restore (keeps the new Kobo's account), covers from .kobo-images (qhash), read-back backup verification + checksum check on restore, settings/fonts/screensavers backup, KoboSource abstraction
  • UI: rewritten in TSX with path routing, English + Italian, accessibility pass, standalone library, history with verify/restore
  • SEO: 17 prerendered pages with localized meta, canonical, hreflang, JSON-LD, real sitemap, 404
  • PWA: installable, offline, streamed downloads through the service worker for Firefox/Safari
  • Docs: README v2.0.0, AGENTS.md

Test plan

  • npm run check (typecheck, lint, 131 tests, build + prerender + service worker)
  • npm run format:check
  • vite preview: clean URLs (/it, /it/ripristino), sitemap, manifest, sw.js
  • Backup + merge restore on a real Kobo
  • Streamed download in Firefox/Safari

Deploy note: the build runs node scripts/postbuild.ts and needs Node 22.18+.

Generated with Devin

Fanfulla and others added 8 commits September 27, 2026 19:59
Drops unused components/helpers and the broken browser-fs-access fallback
(directoryOpen returns File[], not a handle), removes jszip/adm-zip/
better-sqlite3, simplifies .gitignore and moves private real-backup
fixtures to ./fixtures (overridable via KOBUP_FIXTURES_DIR).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- React 19, Vite 8 (Rolldown/Oxc), Vitest 5, Tailwind 4 (CSS-first config),
  Zustand 5, ESLint 10 flat config + typescript-eslint + react-hooks 7 +
  jsx-a11y, Prettier config, GitHub Actions CI (typecheck/lint/format/test/build)
- utils, hooks, store and tests rewritten in strict TypeScript with shared
  domain types; common UI components (Button, Modal on native <dialog>,
  Icon, Checkbox, ...) converted to accessible TSX
- sql.js WASM is now imported via ?url so it is always in sync with the JS
  glue (public/sql-wasm.wasm was 1.13 while the package is 1.14)
- app version injected from package.json (__APP_VERSION__)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…identity

- Kobo stores TimeSpentReading in seconds: values are now converted to
  minutes (stats were inflated 60x, e.g. 1418h shown instead of 23h38m)
- sanitize(): fix "PRAGMA journaling_mode" typo (silently ignored) so the
  restored database really leaves WAL mode
- schema version read from the DbVersion table (PRAGMA user_version is
  always 0 on Kobo databases)
- real model/firmware from .kobo/version (serial number is discarded),
  with the Nickel model id table
- compatibility check returns coded warnings and blocks restoring a newer
  schema onto older firmware
- restore preview computes stats from the backup database (old metadata
  stored seconds labelled as minutes)
- scanning extracted to utils/scan.ts; warns about a pending (non-empty)
  WAL file, recorded in backup metadata
- stats query shares the exact same filter as the book list

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…-hosted fonts

- optional AES-256 password protection for backups (zip.js, streaming);
  restore detects encrypted archives and reports missing/wrong passwords
  (the database contains Kobo account tokens)
- restore saves the current device database (and a pending WAL) to
  .kobo/KoboReader.sqlite.before-restore; undoLastRestore() puts it back
- restore refuses to write "books" into hidden/system folders
  (e.g. books/.kobo/KoboReader.sqlite from a crafted archive)
- vercel.json: strict Content-Security-Policy (connect-src 'self' makes the
  "nothing is uploaded" promise enforced by the browser), nosniff,
  Referrer/Permissions-Policy, immutable caching for hashed assets;
  COEP removed (sql.js never needed SharedArrayBuffer)
- Oswald/Poppins served locally via @fontsource instead of Google Fonts

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ckup

- KoboSource abstraction (File System Access handle or plain FileList) used
  by scanning, backup and covers, with cached directory handles
- covers: look in .kobo-images/<d1>/<d2>/ using Nickel's qhash (current
  firmware) before the legacy .kobo/images; verified against real paths
- restore "merge" mode: copy progress, highlights/notes and collections of
  sideloaded books into the device's own database, keeping its account,
  store library and settings; refuses to run with a pending WAL
- backups are read back after writing: entry list + database SHA-256;
  restore flags archives whose database does not match its checksum
- "Device settings" option now real: Kobo eReader.conf, custom fonts and
  screensavers are backed up under device/ and restored only from a
  whitelist of paths

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…a11y

- History-API router with real URLs (/backup, /it/ripristino, ...),
  crawlable <a href> links, legacy #hash redirects and a 404 page
- typed i18n (English + Italian, same shape enforced by the compiler) with
  localized numbers, dates, sizes, durations, plurals and error messages
- per-page <title>, description, canonical, hreflang and JSON-LD kept in
  sync on navigation
- Backup: shared device session with the Library, real options (books,
  readable highlights, settings/fonts/screensavers), AES-256 password,
  read-back verification, file handle saved for History
- Restore: password unlock, target-device compatibility (model, firmware,
  schema), merge vs replace mode, visible errors, one-click undo
- Library usable on its own: ARIA tabs, lazy covers, collections tab,
  Obsidian/Anki/Markdown exports, book details in an accessible dialog
- History: verify or restore a backup from its stored handle
- works without File System Access (folder input) for backup/library
- accessibility: skip link, focus management, contrast-safe accent colour,
  labelled controls, live regions, keyboard-friendly components
- ZIP/SQLite code loaded on demand; SSR-safe components (every route is
  server-rendered in tests)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…oads

SEO
- every route is prerendered at build time in English and Italian (17 static
  pages) with localized <title>, description, canonical, hreflang
  (en/it/x-default), Open Graph/Twitter tags and JSON-LD (WebApplication,
  WebSite, BreadcrumbList, FAQPage, HowTo); pages hydrate without mismatches
- real sitemap.xml with hreflang alternates (replaces the #hash URLs),
  robots.txt pointing to it, 404.html served with a real 404 status
- correct <html lang> per page; Italian home at /it

PWA / browsers
- web app manifest and generated PNG icons (standard, maskable, Apple)
- service worker precaches pages and hashed assets (works offline) and
  streams backups as downloads in Firefox/Safari (no showSaveFilePicker),
  pulling one chunk at a time so memory stays flat for multi-GB libraries

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- README rewritten for the current app: features, browser support table,
  backup contents/layout, privacy, stack, structure, dev commands, changelog
- AGENTS.md with verification commands, architecture map and project rules
- version 2.0.0 (routes and UI changed); Node 22.18+ for TS scripts
- removed exports left unused by the rewrite and the unused jest-dom package

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@vercel

vercel Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
kobo-offline-backup Ready Ready Preview Sep 27, 2026 7:33pm UTC

@Fanfulla
Fanfulla merged commit 0d56aa2 into main Sep 27, 2026
3 checks passed

This branch was successfully deployed

1 active deployment
Preview — edaaf9d7 Deployed Sep 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant