Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm run format:check
- run: npm test
- run: npm run build
248 changes: 18 additions & 230 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,261 +1,49 @@
# .gitignore for Kobo Backup Manager
# React + Vite + JavaScript project

# Dependencies
node_modules/
/.pnp
.pnp.js
.pnpm-store/

# Build outputs
dist/
dist-ssr/
build/
*.local
*.tsbuildinfo
vite.config.*.timestamp-*

# Logs
logs/
# Logs & caches
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.eslintcache
.cache/
coverage/

# Editor directories and files
# Editors / OS
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea/
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.swp
*.swo
*~

# IDE - VSCode
.vscode/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/

# IDE - IntelliJ / WebStorm
.idea/
*.iml
*.iws
out/

# OS files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
desktop.ini
*.zip
nul

# Environment variables
# Environment
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*.local
.env.*
!.env.example

# Testing
coverage/
.nyc_output/
*.lcov
.coverage
htmlcov/
test-results/
playwright-report/

# Temporary files
tmp/
temp/
*.tmp
*.temp
.cache/

# Package manager files
package-lock.json.backup
yarn.lock.backup
.pnpm-store/
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Vite specific
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

# Build artifacts
*.tsbuildinfo
.eslintcache

# Database files (for local testing)
# Private test fixtures (real Kobo databases / backups contain personal data)
fixtures/
*.zip
*.sqlite
*.sqlite3
*.db
*.db-shm
*.db-wal
*.sqlite-shm
*.sqlite-wal

# Backup files
*.bak
*.backup
*~

# Runtime data
pids/
*.pid
*.seed
*.pid.lock

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Analytics and tracking (if using local config files)
analytics-config.json
tracking-config.json

# User-specific data (backup history stored in browser, but ignore if exported)
backups/
backup-exports/
kobo-backups/

# Documentation build
docs/_build/
docs/.doctrees/

# MacOS
.AppleDouble
.LSOverride
Icon
.DocumentRevisions-V100
.fseventsd
.TemporaryItems
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# Windows
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
*.stackdump
[Dd]esktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msix
*.msm
*.msp
*.lnk

# Linux
.fuse_hidden*
.directory
.Trash-*
.nfs*

# Python (if using Python scripts for development)
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
ENV/
env/
.venv

# Certificates and keys
*.pem
*.key
*.crt
*.p12
*.pfx

# Local development
.local/
local/
scratch/

# Ignore nul file (appears to be an error file)
nul

# Exclude .claude and .planning (local dev workflow files, not needed on GitHub)
# Local dev workflow files (not published)
.claude/
.planning/

# Keep public directory for static assets
!public/

# Keep only README and LICENSE
!README.md
!LICENSE
CONTRIBUTING.md
CODE_OF_CONDUCT.md
KOBO_BACKUP_WEBAPP_BLUEPRINT.md
test-backup.mjs

# Local analysis/testing folders
_backup_analysis/
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dist
dist-ssr
coverage
fixtures
node_modules
package-lock.json
public
7 changes: 7 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"printWidth": 110,
"endOfLine": "auto"
}
36 changes: 36 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# AGENTS.md — KoBup

Privacy-first React/Vite web app that backs up and restores Kobo e-readers entirely in the browser.
Live: https://www.kobup.org (Vercel). Repo: https://github.com/Fanfulla/KoboOfflineBackup

## Commands

- `npm run dev` — dev server (no prerender, no service worker)
- `npm run check` — **run before every commit**: `tsc -b` + ESLint (0 warnings) + Vitest + production build
- `npm run format` / `npm run format:check` — Prettier (CI runs `format:check`)
- `npm run build` — `vite build` + `node scripts/postbuild.ts` (prerender 17 pages, sitemap.xml, sw.js)
- `npm run preview` — serve `dist/` (check `/it`, `/it/ripristino`, `/sw.js`)
- Node 22.18+ is required: `scripts/*.ts` run with Node's native type stripping.

## Architecture

- `src/utils/` — core logic, framework-free and unit-tested:
- `scan.ts` (device scan), `deviceSource.ts` (`KoboSource`: FS Access handle or `webkitdirectory` FileList)
- `backup.ts` (client-zip streaming; zip.js for AES-256; read-back verification), `backupInfo.ts`/`checksum.ts` (light helpers)
- `restore.ts` (parse, full/merge restore, safety snapshot + undo, target inspection), `merge.ts` (reading-data merge)
- `koboDatabase.ts` (sql.js), `koboDevice.ts` (`.kobo/version`), `koboCovers.ts` (`.kobo-images` qhash)
- Heavy modules (`backup.ts`, `restore.ts`, `export.ts`, sql.js) are **dynamically imported** by hooks/pages — keep them out of the initial bundle.
- `src/router/` — History-API router; routes and localized slugs in `routes.ts` (EN: `/restore`, IT: `/it/ripristino`).
- `src/i18n/` — `en.ts` defines the message shape (`Messages`); `it.ts` must match it (compiler + `i18n.test.ts` enforce keys and placeholders). Use `useI18n()` from `core.ts`; never hard-code UI strings.
- `src/seo/` — `meta.ts` (title/description/canonical/hreflang/JSON-LD) is shared by `useDocumentMeta` (client) and `head.ts` (prerender).
- `src/sw/` — service worker (offline precache + streamed downloads for Firefox/Safari) and its page-side protocol.
- State: Zustand store (`stores/koboStore.ts`) — device session in memory, backup history persisted with `skipHydration` (rehydrated after mount).

## Rules

- Components must be SSR-safe: no `window`/`localStorage` during render (use effects, `useSyncExternalStore` server snapshots, `useLocalFlag`). `app.test.tsx` and `hydration.test.tsx` render every route on the server and hydrate it.
- Restore is destructive: keep the safety snapshot, never delete protected dirs (`.kobo`, dot-folders), reject traversal/hidden paths, keep book cleanup opt-in.
- Kobo `TimeSpentReading` is in **seconds** (converted to minutes in `koboDatabase.ts`); the schema version lives in `DbVersion`, not `PRAGMA user_version`.
- Write a failing test first for bug fixes. Real Kobo backups for integration tests go in `fixtures/` or `$KOBUP_FIXTURES_DIR` — **never commit them** (personal data, account tokens).
- CSP in `vercel.json` only allows same-origin connections: do not add third-party scripts, fonts or APIs.
- Adding a page: route in `routes.ts`, component in `pages/`, entry in `App.tsx` `PAGES`, texts in `en.ts` + `it.ts` (`meta.pages`, `nav`), then check the prerendered output.
Loading
Loading