First Commit
This commit is contained in:
1 parent
fa15d48a52
commit
4709096ab3
8 files changed
+2208
No files matched your search
@@ -0,0 +1,212 @@
|
||||
# ✏️ InkNote
|
||||
|
||||
A cross-platform note-taking application with rich text editing, freehand drawing, PDF annotation, and encrypted sync — inspired by Samsung Notes.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Core
|
||||
- **Rich Text Notes** — Bold, italic, underline, headings, bullet/numbered lists, checkboxes
|
||||
- **Freehand Drawing** — Pen, highlighter, eraser with adjustable thickness and color palette (20 colors, 7 pen sizes)
|
||||
- **Undo/Redo** — Full history stack for drawing operations
|
||||
- **PDF Import & Annotation** — Import PDFs, overlay drawing/highlighting, persistent annotations
|
||||
- **Notebooks & Folders** — Create notebooks with custom gradient covers, organize notes by folder
|
||||
- **Star/Favorite** — Quick access to important notes
|
||||
- **Dark & Light Mode** — Full theme support with refined color schemes
|
||||
|
||||
### Sync & Storage
|
||||
- **Offline-First** — Works fully offline, syncs when connection returns
|
||||
- **Conflict-Aware Sync** — Vector clock–based conflict detection; notifies user of merge conflicts
|
||||
- **WebSocket Real-Time Push** — Changes appear on other devices instantly
|
||||
- **Encrypted at Rest** — AES-256-CBC encryption for all note content on the server
|
||||
- **JWT Authentication** — Secure user accounts with bcrypt password hashing
|
||||
|
||||
### Architecture
|
||||
- **Self-Hosted Backend** — Node.js + Express + SQLite (or PostgreSQL)
|
||||
- **Docker Ready** — docker-compose up and you're running
|
||||
- **Client Sync Engine** — Portable JS library for any frontend (React Native, Flutter JS bridge, Electron, web)
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
inknote/
|
||||
├── note-app.jsx # Full React UI (artifact / web)
|
||||
├── docker-compose.yml # Container orchestration
|
||||
├── backend/
|
||||
│ ├── server.js # Express API + WebSocket server
|
||||
│ ├── sync-client.js # Client-side sync engine library
|
||||
│ ├── package.json # Node dependencies
|
||||
│ ├── Dockerfile # Server container
|
||||
│ └── .env.example # Environment variable template
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Run the UI (Web / Artifact)
|
||||
|
||||
The `note-app.jsx` file is a self-contained React component. It runs directly as a Claude artifact, or you can embed it in any React project:
|
||||
|
||||
```jsx
|
||||
import NoteApp from './note-app';
|
||||
|
||||
function App() {
|
||||
return <NoteApp />;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Run the Backend
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
cp .env.example .env # Edit secrets!
|
||||
npm install
|
||||
npm start # → http://localhost:4000
|
||||
```
|
||||
|
||||
### 3. Docker Deployment
|
||||
|
||||
```bash
|
||||
# Generate a secure encryption key
|
||||
export ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
export JWT_SECRET=$(openssl rand -base64 32)
|
||||
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
The server runs on port `4000`, the web UI on port `3000`.
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### Authentication
|
||||
|
||||
| Method | Endpoint | Body | Response |
|
||||
|--------|-----------------------|-------------------------------|---------------------|
|
||||
| POST | `/api/auth/register` | `{email, password, displayName}` | `{token, user}` |
|
||||
| POST | `/api/auth/login` | `{email, password}` | `{token, user}` |
|
||||
|
||||
### Notebooks
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|-----------------------|--------------------------|
|
||||
| GET | `/api/notebooks` | List all notebooks |
|
||||
| POST | `/api/notebooks` | Create notebook |
|
||||
| PUT | `/api/notebooks/:id` | Update notebook |
|
||||
| DELETE | `/api/notebooks/:id` | Soft-delete notebook |
|
||||
|
||||
### Notes
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|---------------------------|-------------------------------|
|
||||
| GET | `/api/notes` | List all notes (metadata) |
|
||||
| GET | `/api/notes/:id` | Get note with decrypted content |
|
||||
| POST | `/api/notes` | Create note |
|
||||
| PUT | `/api/notes/:id` | Update note (conflict-aware) |
|
||||
| DELETE | `/api/notes/:id` | Soft-delete note |
|
||||
| PUT | `/api/notes/:id/drawing` | Save drawing data (base64) |
|
||||
| POST | `/api/notes/pdf` | Upload PDF (multipart/form) |
|
||||
|
||||
### Sync
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|------------- |------------------------------------------|
|
||||
| POST | `/api/sync` | Bidirectional sync with conflict reports |
|
||||
|
||||
### WebSocket
|
||||
|
||||
Connect to `ws://localhost:4000/ws`, then send:
|
||||
```json
|
||||
{"type": "auth", "token": "JWT_TOKEN", "deviceId": "device-123"}
|
||||
```
|
||||
|
||||
Receive real-time events: `note_created`, `note_updated`, `note_deleted`, `notebook_created`, etc.
|
||||
|
||||
---
|
||||
|
||||
## Sync Engine (Client)
|
||||
|
||||
```javascript
|
||||
import { SyncEngine } from './sync-client';
|
||||
|
||||
const sync = new SyncEngine('https://your-server.com', 'device-abc');
|
||||
|
||||
// Authenticate
|
||||
await sync.authenticate('user@example.com', 'password');
|
||||
|
||||
// Listen for events
|
||||
sync.on('status', (status) => console.log('Sync:', status));
|
||||
sync.on('conflict', (conflict) => {
|
||||
// Present both versions to user, let them choose
|
||||
console.log('Conflict:', conflict);
|
||||
});
|
||||
sync.on('remote_change', (change) => {
|
||||
// Update local state with remote changes
|
||||
console.log('Remote change:', change);
|
||||
});
|
||||
|
||||
// Start syncing (every 30s + WebSocket push)
|
||||
sync.start();
|
||||
|
||||
// CRUD operations auto-queue for sync
|
||||
await sync.createNote({ type: 'text', title: 'Hello', content: '<p>World</p>' });
|
||||
await sync.updateNote('note-id', { title: 'Updated' });
|
||||
await sync.deleteNote('note-id');
|
||||
await sync.uploadPDF(fileBlob);
|
||||
await sync.saveDrawing('note-id', canvasDataUrl);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
InkNote uses **vector clocks** to detect conflicts:
|
||||
|
||||
1. Each device maintains a logical clock counter
|
||||
2. On save, the device increments its counter and includes the full vector clock
|
||||
3. The server compares clocks — if the client clock is strictly newer, the update applies
|
||||
4. If clocks are concurrent (both have changes the other doesn't), a **409 Conflict** is returned
|
||||
5. The client receives both versions and can present a merge UI to the user
|
||||
|
||||
---
|
||||
|
||||
## Extending to Native Platforms
|
||||
|
||||
### React Native
|
||||
Use `note-app.jsx` components with React Native adaptations (replace HTML canvas with `react-native-canvas` or `@shopify/react-native-skia` for drawing). The sync engine works as-is.
|
||||
|
||||
### Flutter
|
||||
Use the sync engine via a JavaScript bridge, or port the API calls to Dart. For drawing, use Flutter's `CustomPainter`. For PDF, use `flutter_pdfview`.
|
||||
|
||||
### Electron
|
||||
Wrap the React app directly in Electron for desktop. Add native file system access for PDF import/export.
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
- All note content is encrypted with AES-256-CBC before database storage
|
||||
- Passwords are hashed with bcrypt (12 rounds)
|
||||
- JWT tokens expire after 30 days
|
||||
- HTTPS should be enforced in production (use a reverse proxy like Nginx or Pangolin)
|
||||
- The encryption key should be stored securely (e.g., environment variable, secrets manager)
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-----------|-------------------------------------|
|
||||
| Frontend | React (web), adaptable to RN/Flutter |
|
||||
| Backend | Node.js, Express |
|
||||
| Database | SQLite (dev), PostgreSQL (prod) |
|
||||
| Real-time | WebSocket (ws library) |
|
||||
| Auth | JWT + bcrypt |
|
||||
| Encryption| AES-256-CBC |
|
||||
| Container | Docker + docker-compose |
|
||||
Reference in new issue
Block a user