markdown-notes-engine 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +576 -88
- package/lib/README.md +123 -2
- package/lib/backend/db/connection.js +127 -0
- package/lib/backend/db/schema.sql +144 -0
- package/lib/backend/github.js +2 -4
- package/lib/backend/index.js +69 -28
- package/lib/backend/markdown.js +4 -6
- package/lib/backend/routes/notes.js +35 -4
- package/lib/backend/routes/search.js +2 -2
- package/lib/backend/routes/upload.js +33 -6
- package/lib/backend/storage.js +2 -4
- package/lib/backend/version-control.js +458 -0
- package/lib/frontend/index.js +3 -6
- package/lib/index.js +5 -16
- package/package.json +23 -5
- package/lib/frontend/styles.css +0 -431
package/README.md
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
# Markdown Notes Engine
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/markdown-notes-engine)
|
|
3
|
+
[](https://www.npmjs.com/package/@camthomp1/markdown-notes-engine)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
A complete, production-ready markdown note-taking engine with GitHub integration and media hosting. Add powerful note-taking capabilities to any Node.js application with just a few lines of code.
|
|
6
|
+
A complete, production-ready markdown note-taking engine with Git-like version control (PostgreSQL) or GitHub integration and media hosting (Cloudflare R2/AWS S3). Add powerful note-taking capabilities to any Node.js application with just a few lines of code.
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
10
|
- **Full Markdown Support** - Rich markdown editing with live preview
|
|
11
|
-
- **
|
|
11
|
+
- **Git-Like Version Control** - Choose between PostgreSQL (recommended) or GitHub for version control
|
|
12
|
+
- **PostgreSQL**: Content-addressable storage with SHA-256 hashing, commit history, and branching
|
|
13
|
+
- **GitHub**: Traditional GitHub-based storage (legacy support)
|
|
14
|
+
- **Content-Addressable Storage** - Deduplication and integrity verification
|
|
15
|
+
- **Full Commit History** - Track every change with parent references and timestamps
|
|
12
16
|
- **Media Hosting** - Image and video uploads to Cloudflare R2 or AWS S3
|
|
17
|
+
- **Full-Text Search** - PostgreSQL tsvector search or GitHub code search
|
|
13
18
|
- **Syntax Highlighting** - Beautiful code blocks with highlight.js
|
|
14
19
|
- **Auto-save** - Never lose your work with automatic saving
|
|
15
|
-
- **
|
|
16
|
-
- **Dark Mode** - Built-in dark mode support
|
|
20
|
+
- **Dark Mode** - Built-in dark mode support with persistence
|
|
17
21
|
- **Responsive** - Works beautifully on mobile and desktop
|
|
18
22
|
- **Keyboard Shortcuts** - Boost productivity with keyboard shortcuts
|
|
19
23
|
- **Drag & Drop** - Upload media with drag and drop
|
|
24
|
+
- **File History** - Get complete commit history for any file
|
|
20
25
|
|
|
21
26
|
## Quick Start
|
|
22
27
|
|
|
@@ -26,16 +31,108 @@ A complete, production-ready markdown note-taking engine with GitHub integration
|
|
|
26
31
|
npm install markdown-notes-engine
|
|
27
32
|
```
|
|
28
33
|
|
|
29
|
-
###
|
|
34
|
+
### Basic Usage
|
|
35
|
+
|
|
36
|
+
This package is ESM-only. Use ES module `import` statements:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import express from 'express';
|
|
40
|
+
import { createNotesRouter } from 'markdown-notes-engine';
|
|
41
|
+
|
|
42
|
+
const app = express();
|
|
43
|
+
|
|
44
|
+
async function startServer() {
|
|
45
|
+
const notesRouter = await createNotesRouter({
|
|
46
|
+
database: {
|
|
47
|
+
host: 'localhost',
|
|
48
|
+
port: 5432,
|
|
49
|
+
database: 'markdown_notes',
|
|
50
|
+
user: 'postgres',
|
|
51
|
+
password: 'your_password',
|
|
52
|
+
branch: 'main',
|
|
53
|
+
author: 'your_name'
|
|
54
|
+
},
|
|
55
|
+
storage: {
|
|
56
|
+
type: 'r2',
|
|
57
|
+
accountId: process.env.R2_ACCOUNT_ID,
|
|
58
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID,
|
|
59
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
|
|
60
|
+
bucketName: process.env.R2_BUCKET_NAME,
|
|
61
|
+
publicUrl: process.env.R2_PUBLIC_URL
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
app.use('/api/notes', notesRouter);
|
|
66
|
+
app.listen(3000, () => {
|
|
67
|
+
console.log('Notes engine running on http://localhost:3000');
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
startServer();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Version Control Options
|
|
75
|
+
|
|
76
|
+
### Option 1: PostgreSQL (Recommended)
|
|
77
|
+
|
|
78
|
+
PostgreSQL-backed version control with Git-like architecture:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
import { createNotesRouter } from 'markdown-notes-engine';
|
|
82
|
+
|
|
83
|
+
const notesRouter = await createNotesRouter({
|
|
84
|
+
database: {
|
|
85
|
+
// Option A: Connection string (Supabase, Heroku, etc.)
|
|
86
|
+
connectionString: process.env.DATABASE_URL,
|
|
87
|
+
branch: 'main',
|
|
88
|
+
author: 'your_name'
|
|
89
|
+
|
|
90
|
+
// Option B: Individual parameters (local PostgreSQL)
|
|
91
|
+
// host: 'localhost',
|
|
92
|
+
// port: 5432,
|
|
93
|
+
// database: 'markdown_notes',
|
|
94
|
+
// user: 'postgres',
|
|
95
|
+
// password: 'your_password',
|
|
96
|
+
// branch: 'main',
|
|
97
|
+
// author: 'your_name'
|
|
98
|
+
},
|
|
99
|
+
storage: {
|
|
100
|
+
type: 'r2', // or 's3'
|
|
101
|
+
accountId: process.env.R2_ACCOUNT_ID,
|
|
102
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID,
|
|
103
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
|
|
104
|
+
bucketName: process.env.R2_BUCKET_NAME,
|
|
105
|
+
publicUrl: process.env.R2_PUBLIC_URL
|
|
106
|
+
},
|
|
107
|
+
options: {
|
|
108
|
+
autoInitSchema: true, // Auto-initialize database schema
|
|
109
|
+
autoUpdateReadme: true // Auto-update README.md with note list
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**PostgreSQL Features:**
|
|
115
|
+
- Content-addressable storage (SHA-256 hashing)
|
|
116
|
+
- Full commit history with parent references
|
|
117
|
+
- Branches support
|
|
118
|
+
- No size limits
|
|
119
|
+
- Full-text search with PostgreSQL tsvector
|
|
120
|
+
- Automatic schema migrations
|
|
121
|
+
- Works with local PostgreSQL, Supabase, Heroku Postgres, etc.
|
|
122
|
+
|
|
123
|
+
### Option 2: GitHub (Legacy)
|
|
124
|
+
|
|
125
|
+
GitHub-based version control:
|
|
30
126
|
|
|
31
127
|
```javascript
|
|
32
|
-
|
|
128
|
+
import { createNotesRouter } from 'markdown-notes-engine';
|
|
33
129
|
|
|
34
|
-
const notesRouter = createNotesRouter({
|
|
130
|
+
const notesRouter = await createNotesRouter({
|
|
35
131
|
github: {
|
|
36
132
|
token: process.env.GITHUB_TOKEN,
|
|
37
133
|
owner: process.env.GITHUB_OWNER,
|
|
38
|
-
repo: process.env.GITHUB_REPO
|
|
134
|
+
repo: process.env.GITHUB_REPO,
|
|
135
|
+
branch: 'main'
|
|
39
136
|
},
|
|
40
137
|
storage: {
|
|
41
138
|
type: 'r2',
|
|
@@ -46,141 +143,518 @@ const notesRouter = createNotesRouter({
|
|
|
46
143
|
publicUrl: process.env.R2_PUBLIC_URL
|
|
47
144
|
}
|
|
48
145
|
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Note:** GitHub mode requires the optional `@octokit/rest` dependency:
|
|
149
|
+
```bash
|
|
150
|
+
npm install @octokit/rest
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Remote Database Support
|
|
154
|
+
|
|
155
|
+
Works with any PostgreSQL database:
|
|
156
|
+
|
|
157
|
+
### Supabase
|
|
158
|
+
```javascript
|
|
159
|
+
{
|
|
160
|
+
database: {
|
|
161
|
+
connectionString: process.env.DATABASE_URL, // Use Connection Pooling URL (port 6543)
|
|
162
|
+
branch: 'main',
|
|
163
|
+
author: 'your_name'
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
See [docs/SUPABASE.md](docs/SUPABASE.md) for detailed Supabase setup instructions.
|
|
49
169
|
|
|
50
|
-
|
|
170
|
+
### Heroku Postgres
|
|
171
|
+
```javascript
|
|
172
|
+
{
|
|
173
|
+
database: {
|
|
174
|
+
connectionString: process.env.DATABASE_URL,
|
|
175
|
+
ssl: 'require',
|
|
176
|
+
branch: 'main',
|
|
177
|
+
author: 'your_name'
|
|
178
|
+
}
|
|
179
|
+
}
|
|
51
180
|
```
|
|
52
181
|
|
|
53
|
-
###
|
|
182
|
+
### Local PostgreSQL
|
|
183
|
+
```javascript
|
|
184
|
+
{
|
|
185
|
+
database: {
|
|
186
|
+
host: 'localhost',
|
|
187
|
+
port: 5432,
|
|
188
|
+
database: 'markdown_notes',
|
|
189
|
+
user: 'postgres',
|
|
190
|
+
password: 'your_password',
|
|
191
|
+
branch: 'main',
|
|
192
|
+
author: 'your_name'
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Frontend Integration
|
|
198
|
+
|
|
199
|
+
### Using the Bundled Editor
|
|
54
200
|
|
|
55
201
|
```html
|
|
56
|
-
|
|
202
|
+
<!DOCTYPE html>
|
|
203
|
+
<html>
|
|
204
|
+
<head>
|
|
205
|
+
<title>My Notes App</title>
|
|
206
|
+
<link rel="stylesheet" href="node_modules/markdown-notes-engine/lib/frontend/styles.css">
|
|
207
|
+
</head>
|
|
208
|
+
<body>
|
|
209
|
+
<div id="notes-app"></div>
|
|
210
|
+
|
|
211
|
+
<script type="module">
|
|
212
|
+
import { NotesEditor } from 'markdown-notes-engine/frontend';
|
|
213
|
+
|
|
214
|
+
const editor = new NotesEditor({
|
|
215
|
+
container: '#notes-app',
|
|
216
|
+
apiEndpoint: '/api/notes',
|
|
217
|
+
theme: 'dark',
|
|
218
|
+
autoSave: true,
|
|
219
|
+
autoSaveDelay: 2000
|
|
220
|
+
});
|
|
221
|
+
</script>
|
|
222
|
+
</body>
|
|
223
|
+
</html>
|
|
224
|
+
```
|
|
57
225
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
226
|
+
### Custom Frontend
|
|
227
|
+
|
|
228
|
+
Build your own frontend using the API endpoints:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// Get file structure
|
|
232
|
+
const structure = await fetch('/api/notes/structure').then(r => r.json());
|
|
233
|
+
|
|
234
|
+
// Get note content
|
|
235
|
+
const note = await fetch('/api/notes/note?path=my-note.md').then(r => r.json());
|
|
236
|
+
|
|
237
|
+
// Save note
|
|
238
|
+
await fetch('/api/notes/note', {
|
|
239
|
+
method: 'POST',
|
|
240
|
+
headers: { 'Content-Type': 'application/json' },
|
|
241
|
+
body: JSON.stringify({ path: 'my-note.md', content: '# Hello World' })
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Delete note (supports both query params and body)
|
|
245
|
+
await fetch('/api/notes/note?path=my-note.md', { method: 'DELETE' });
|
|
246
|
+
// or
|
|
247
|
+
await fetch('/api/notes/note', {
|
|
248
|
+
method: 'DELETE',
|
|
249
|
+
headers: { 'Content-Type': 'application/json' },
|
|
250
|
+
body: JSON.stringify({ path: 'my-note.md' })
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Get file history (PostgreSQL only)
|
|
254
|
+
const history = await fetch('/api/notes/history?path=my-note.md').then(r => r.json());
|
|
255
|
+
|
|
256
|
+
// Search notes
|
|
257
|
+
const results = await fetch('/api/notes/search?q=hello').then(r => r.json());
|
|
65
258
|
```
|
|
66
259
|
|
|
67
|
-
##
|
|
260
|
+
## API Reference
|
|
261
|
+
|
|
262
|
+
### Complete API Endpoints
|
|
263
|
+
|
|
264
|
+
| Method | Endpoint | Description | Params |
|
|
265
|
+
|--------|----------|-------------|--------|
|
|
266
|
+
| GET | `/structure` | Get file tree | - |
|
|
267
|
+
| GET | `/note` | Get note content | `?path=file.md` |
|
|
268
|
+
| POST | `/note` | Save/update note | `{ path, content }` |
|
|
269
|
+
| DELETE | `/note` | Delete note | `?path=file.md` or `{ path }` |
|
|
270
|
+
| POST | `/folder` | Create folder | `{ path }` |
|
|
271
|
+
| DELETE | `/folder` | Delete folder | `{ path }` |
|
|
272
|
+
| GET | `/history` | Get file commit history | `?path=file.md` (PostgreSQL only) |
|
|
273
|
+
| GET | `/search` | Full-text search | `?q=query` |
|
|
274
|
+
| POST | `/upload-image` | Upload image | FormData with `image` file |
|
|
275
|
+
| POST | `/upload-video` | Upload video | FormData with `video` file |
|
|
276
|
+
| POST | `/upload-image-base64` | Upload base64 image | `{ image, filename, folder }` |
|
|
277
|
+
| POST | `/render` | Render markdown | `{ markdown }` |
|
|
278
|
+
|
|
279
|
+
### Response Formats
|
|
280
|
+
|
|
281
|
+
**Structure Response:**
|
|
282
|
+
```json
|
|
283
|
+
{
|
|
284
|
+
"structure": [
|
|
285
|
+
{
|
|
286
|
+
"name": "folder",
|
|
287
|
+
"type": "folder",
|
|
288
|
+
"path": "folder",
|
|
289
|
+
"children": [
|
|
290
|
+
{
|
|
291
|
+
"name": "note.md",
|
|
292
|
+
"type": "file",
|
|
293
|
+
"path": "folder/note.md"
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
```
|
|
68
300
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
301
|
+
**Note Response:**
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"path": "my-note.md",
|
|
305
|
+
"content": "# Hello World\n\nThis is my note.",
|
|
306
|
+
"sha": "abc123..."
|
|
307
|
+
}
|
|
308
|
+
```
|
|
72
309
|
|
|
73
|
-
|
|
310
|
+
**Save Response:**
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"success": true,
|
|
314
|
+
"path": "my-note.md",
|
|
315
|
+
"sha": "def456...",
|
|
316
|
+
"commit": {
|
|
317
|
+
"sha": "commit-sha",
|
|
318
|
+
"message": "Update my-note.md"
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
74
322
|
|
|
75
|
-
|
|
323
|
+
**History Response (PostgreSQL):**
|
|
324
|
+
```json
|
|
325
|
+
[
|
|
326
|
+
{
|
|
327
|
+
"sha": "commit-id",
|
|
328
|
+
"message": "Update note",
|
|
329
|
+
"author": "user",
|
|
330
|
+
"date": "2025-01-03T12:00:00.000Z",
|
|
331
|
+
"blobHash": "content-hash",
|
|
332
|
+
"changed": true
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
```
|
|
76
336
|
|
|
77
|
-
|
|
78
|
-
|
|
337
|
+
**Search Response:**
|
|
338
|
+
```json
|
|
339
|
+
{
|
|
340
|
+
"results": [
|
|
341
|
+
{
|
|
342
|
+
"path": "my-note.md",
|
|
343
|
+
"content": "# Hello World",
|
|
344
|
+
"score": 0.95,
|
|
345
|
+
"preview": "...matching text..."
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
```
|
|
79
350
|
|
|
80
|
-
|
|
351
|
+
**Upload Response:**
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"success": true,
|
|
355
|
+
"url": "https://your-bucket.r2.dev/images/abc123.jpg",
|
|
356
|
+
"path": "images/abc123.jpg",
|
|
357
|
+
"filename": "abc123.jpg"
|
|
358
|
+
}
|
|
359
|
+
```
|
|
81
360
|
|
|
82
|
-
|
|
361
|
+
## Configuration Options
|
|
83
362
|
|
|
84
|
-
###
|
|
85
|
-
- **`createNotesRouter(config)`** - Express router with all API endpoints
|
|
86
|
-
- **`GitHubClient`** - GitHub API wrapper for note storage
|
|
87
|
-
- **`StorageClient`** - R2/S3 client for media uploads
|
|
88
|
-
- **`MarkdownRenderer`** - Markdown to HTML renderer with syntax highlighting
|
|
363
|
+
### Database Configuration
|
|
89
364
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
365
|
+
```javascript
|
|
366
|
+
{
|
|
367
|
+
database: {
|
|
368
|
+
// Connection string (recommended for remote databases)
|
|
369
|
+
connectionString: 'postgresql://user:pass@host:5432/db',
|
|
370
|
+
|
|
371
|
+
// Or individual parameters
|
|
372
|
+
host: 'localhost',
|
|
373
|
+
port: 5432,
|
|
374
|
+
database: 'markdown_notes',
|
|
375
|
+
user: 'postgres',
|
|
376
|
+
password: 'your_password',
|
|
377
|
+
|
|
378
|
+
// Connection pool settings
|
|
379
|
+
maxConnections: 20,
|
|
380
|
+
idleTimeout: 30,
|
|
381
|
+
connectionTimeout: 10,
|
|
382
|
+
|
|
383
|
+
// SSL configuration
|
|
384
|
+
ssl: 'prefer', // 'require', 'prefer', or false
|
|
385
|
+
|
|
386
|
+
// Version control settings
|
|
387
|
+
branch: 'main',
|
|
388
|
+
author: 'your_name'
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
93
392
|
|
|
94
|
-
|
|
393
|
+
### Storage Configuration
|
|
95
394
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
395
|
+
**Cloudflare R2:**
|
|
396
|
+
```javascript
|
|
397
|
+
{
|
|
398
|
+
storage: {
|
|
399
|
+
type: 'r2',
|
|
400
|
+
accountId: 'your-account-id',
|
|
401
|
+
accessKeyId: 'your-access-key-id',
|
|
402
|
+
secretAccessKey: 'your-secret-key',
|
|
403
|
+
bucketName: 'your-bucket',
|
|
404
|
+
publicUrl: 'https://pub-xxxxx.r2.dev'
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
99
408
|
|
|
100
|
-
|
|
409
|
+
**AWS S3:**
|
|
410
|
+
```javascript
|
|
411
|
+
{
|
|
412
|
+
storage: {
|
|
413
|
+
type: 's3',
|
|
414
|
+
region: 'us-east-1',
|
|
415
|
+
accessKeyId: 'your-access-key-id',
|
|
416
|
+
secretAccessKey: 'your-secret-key',
|
|
417
|
+
bucketName: 'your-bucket',
|
|
418
|
+
publicUrl: 'https://your-bucket.s3.amazonaws.com'
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
101
422
|
|
|
102
|
-
|
|
103
|
-
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
|
|
104
|
-
GITHUB_OWNER=your-username
|
|
105
|
-
GITHUB_REPO=your-notes-repo
|
|
423
|
+
### Options
|
|
106
424
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
425
|
+
```javascript
|
|
426
|
+
{
|
|
427
|
+
options: {
|
|
428
|
+
autoInitSchema: true, // Automatically initialize database schema
|
|
429
|
+
autoUpdateReadme: true // Auto-update README.md with note list
|
|
430
|
+
}
|
|
431
|
+
}
|
|
112
432
|
```
|
|
113
433
|
|
|
114
|
-
##
|
|
434
|
+
## Database Schema
|
|
115
435
|
|
|
116
|
-
|
|
436
|
+
The PostgreSQL version control uses a Git-like architecture:
|
|
437
|
+
|
|
438
|
+
- **blobs**: Content storage (content-addressable by SHA-256)
|
|
439
|
+
- **commits**: Commit records with messages, authors, and timestamps
|
|
440
|
+
- **trees**: File path to blob hash mappings for each commit
|
|
441
|
+
- **branches**: Named pointers to commits (like Git branches)
|
|
442
|
+
- **media**: Uploaded media file references
|
|
443
|
+
- **search_index**: Full-text search index using PostgreSQL tsvector
|
|
444
|
+
|
|
445
|
+
See [lib/backend/db/schema.sql](lib/backend/db/schema.sql) for the complete schema.
|
|
446
|
+
|
|
447
|
+
## Migration Guide
|
|
448
|
+
|
|
449
|
+
If you have an existing database from an older version, the schema includes automatic migrations. Simply restart your application with `autoInitSchema: true` and the migrations will run automatically.
|
|
450
|
+
|
|
451
|
+
See [MIGRATION.md](MIGRATION.md) for detailed migration instructions.
|
|
452
|
+
|
|
453
|
+
## Examples
|
|
454
|
+
|
|
455
|
+
Check out the `/examples` directory for complete working examples:
|
|
456
|
+
|
|
457
|
+
- **[postgres-example.js](examples/postgres-example.js)** - PostgreSQL version control with full Git-like history
|
|
458
|
+
- **[Express App](examples/express-app/)** - GitHub-based version control (legacy)
|
|
459
|
+
- **[.env.example](examples/.env.example)** - Environment variable template
|
|
460
|
+
|
|
461
|
+
## Version Control Comparison
|
|
462
|
+
|
|
463
|
+
| Feature | PostgreSQL | GitHub |
|
|
464
|
+
|---------|-----------|--------|
|
|
465
|
+
| Storage Limits | Unlimited | 100GB per repo |
|
|
466
|
+
| API Dependencies | None | GitHub API required |
|
|
467
|
+
| Speed | Fast (local or pooled DB) | Network dependent |
|
|
468
|
+
| Search | PostgreSQL full-text | GitHub code search |
|
|
469
|
+
| Control | Full ownership | External service |
|
|
470
|
+
| Cost | Database hosting | Free for public repos |
|
|
471
|
+
| Version History | Full Git-like history | Native Git history |
|
|
472
|
+
| File History | ✅ Built-in | ❌ Not available |
|
|
473
|
+
| Branching | ✅ Supported | ✅ Supported |
|
|
474
|
+
| Content Deduplication | ✅ SHA-256 hashing | ❌ No |
|
|
475
|
+
|
|
476
|
+
## Architecture
|
|
477
|
+
|
|
478
|
+
### Backend Modules
|
|
479
|
+
|
|
480
|
+
- **`createNotesRouter(config)`** - Express router factory (async function)
|
|
481
|
+
- **`VersionControlClient`** - PostgreSQL-backed Git-like version control
|
|
482
|
+
- **`DatabaseConnection`** - PostgreSQL connection pool manager (uses `postgres` package)
|
|
483
|
+
- **`GitHubClient`** - GitHub API wrapper for note storage (legacy, requires `@octokit/rest`)
|
|
484
|
+
- **`StorageClient`** - R2/S3 client for media uploads (uses AWS SDK)
|
|
485
|
+
- **`MarkdownRenderer`** - Markdown to HTML renderer with syntax highlighting
|
|
486
|
+
|
|
487
|
+
### Frontend Component
|
|
488
|
+
|
|
489
|
+
- **`NotesEditor`** - Complete note-taking UI component with:
|
|
490
|
+
- Markdown editor with live preview
|
|
491
|
+
- File tree navigation
|
|
492
|
+
- Dark mode support
|
|
493
|
+
- Keyboard shortcuts
|
|
494
|
+
- Drag & drop media uploads
|
|
495
|
+
- Auto-save functionality
|
|
117
496
|
|
|
118
497
|
### Project Structure
|
|
119
498
|
|
|
120
499
|
```
|
|
121
500
|
markdown-notes-engine/
|
|
122
|
-
├── lib/
|
|
123
|
-
│ ├── backend/
|
|
124
|
-
│ ├──
|
|
125
|
-
│
|
|
126
|
-
|
|
127
|
-
│
|
|
128
|
-
├──
|
|
129
|
-
├──
|
|
130
|
-
└──
|
|
501
|
+
├── lib/ # NPM package source (ESM)
|
|
502
|
+
│ ├── backend/ # Express router and API
|
|
503
|
+
│ │ ├── db/ # Database schema and connection
|
|
504
|
+
│ │ │ ├── connection.js # PostgreSQL connection manager
|
|
505
|
+
│ │ │ └── schema.sql # Database schema with migrations
|
|
506
|
+
│ │ ├── routes/ # API route handlers
|
|
507
|
+
│ │ │ ├── notes.js # Note CRUD operations
|
|
508
|
+
│ │ │ ├── search.js # Search functionality
|
|
509
|
+
│ │ │ └── upload.js # Media upload handlers
|
|
510
|
+
│ │ ├── index.js # Main backend entry point
|
|
511
|
+
│ │ ├── github.js # GitHub client (legacy)
|
|
512
|
+
│ │ ├── version-control.js # PostgreSQL version control
|
|
513
|
+
│ │ ├── storage.js # S3/R2 storage client
|
|
514
|
+
│ │ └── markdown.js # Markdown renderer
|
|
515
|
+
│ ├── frontend/ # Editor UI component
|
|
516
|
+
│ │ ├── index.js # NotesEditor class
|
|
517
|
+
│ │ └── styles.css # Styles and dark mode
|
|
518
|
+
│ └── index.js # Main package entry point
|
|
519
|
+
├── examples/ # Usage examples
|
|
520
|
+
│ ├── postgres-example.js # PostgreSQL example
|
|
521
|
+
│ ├── .env.example # Environment template
|
|
522
|
+
│ └── express-app/ # GitHub integration example
|
|
523
|
+
├── docs/ # Documentation
|
|
524
|
+
│ └── SUPABASE.md # Supabase setup guide
|
|
525
|
+
├── MIGRATION.md # Database migration guide
|
|
526
|
+
└── package.json # Package configuration (ESM)
|
|
131
527
|
```
|
|
132
528
|
|
|
529
|
+
## Requirements
|
|
530
|
+
|
|
531
|
+
- **Node.js** >= 16.0.0
|
|
532
|
+
- **For PostgreSQL version control** (recommended):
|
|
533
|
+
- PostgreSQL database (version 12 or higher)
|
|
534
|
+
- Works with local PostgreSQL, Supabase, Heroku Postgres, etc.
|
|
535
|
+
- **For GitHub version control** (legacy):
|
|
536
|
+
- GitHub personal access token with `repo` scope
|
|
537
|
+
- Install optional dependency: `npm install @octokit/rest`
|
|
538
|
+
- **For media hosting**:
|
|
539
|
+
- Cloudflare R2 or AWS S3 account
|
|
540
|
+
|
|
541
|
+
## Keyboard Shortcuts
|
|
542
|
+
|
|
543
|
+
- `Ctrl/Cmd + S` - Save note
|
|
544
|
+
- `Ctrl/Cmd + P` - Toggle preview mode
|
|
545
|
+
- `Ctrl/Cmd + N` - New note
|
|
546
|
+
- `Ctrl/Cmd + U` - Upload image
|
|
547
|
+
- `Ctrl/Cmd + Shift + U` - Upload video
|
|
548
|
+
|
|
549
|
+
## Development
|
|
550
|
+
|
|
133
551
|
### Running the Demo
|
|
134
552
|
|
|
135
553
|
```bash
|
|
136
554
|
# Install dependencies
|
|
137
555
|
npm install
|
|
138
556
|
|
|
139
|
-
#
|
|
140
|
-
cp
|
|
557
|
+
# Copy environment template
|
|
558
|
+
cp examples/.env.example .env
|
|
559
|
+
|
|
560
|
+
# Edit .env with your credentials
|
|
561
|
+
# Set up PostgreSQL database or GitHub token
|
|
562
|
+
# Configure R2/S3 storage
|
|
141
563
|
|
|
142
|
-
# Start the server
|
|
564
|
+
# Start the development server
|
|
143
565
|
npm run dev
|
|
144
566
|
```
|
|
145
567
|
|
|
146
|
-
Open `http://localhost:
|
|
568
|
+
Open `http://localhost:3000` to see the demo application.
|
|
147
569
|
|
|
148
|
-
|
|
570
|
+
### Building the Package
|
|
149
571
|
|
|
150
|
-
|
|
572
|
+
The package is already built and ready to use. All source files are in the `lib/` directory and use ES modules.
|
|
151
573
|
|
|
152
|
-
|
|
153
|
-
- `GET /note` - Get note content
|
|
154
|
-
- `POST /note` - Save note
|
|
155
|
-
- `DELETE /note` - Delete note
|
|
156
|
-
- `POST /folder` - Create folder
|
|
157
|
-
- `DELETE /folder` - Delete folder
|
|
158
|
-
- `POST /upload-image` - Upload image
|
|
159
|
-
- `POST /upload-video` - Upload video
|
|
160
|
-
- `POST /render` - Render markdown
|
|
161
|
-
- `GET /search` - Search notes
|
|
574
|
+
## Environment Variables
|
|
162
575
|
|
|
163
|
-
|
|
576
|
+
### PostgreSQL Setup
|
|
164
577
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
578
|
+
```env
|
|
579
|
+
# Database Configuration
|
|
580
|
+
DB_HOST=localhost
|
|
581
|
+
DB_PORT=5432
|
|
582
|
+
DB_NAME=markdown_notes
|
|
583
|
+
DB_USER=postgres
|
|
584
|
+
DB_PASSWORD=your_password_here
|
|
585
|
+
|
|
586
|
+
# Or use connection string (Supabase, Heroku, etc.)
|
|
587
|
+
# DATABASE_URL=postgresql://postgres:password@host:6543/postgres
|
|
588
|
+
|
|
589
|
+
# Storage Configuration (R2 or S3)
|
|
590
|
+
R2_ACCOUNT_ID=your_account_id
|
|
591
|
+
R2_ACCESS_KEY_ID=your_access_key_id
|
|
592
|
+
R2_SECRET_ACCESS_KEY=your_secret_access_key
|
|
593
|
+
R2_BUCKET_NAME=your_bucket_name
|
|
594
|
+
R2_PUBLIC_URL=https://pub-xxxxxx.r2.dev
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### GitHub Setup (Legacy)
|
|
598
|
+
|
|
599
|
+
```env
|
|
600
|
+
# GitHub Configuration
|
|
601
|
+
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
|
|
602
|
+
GITHUB_OWNER=your-username
|
|
603
|
+
GITHUB_REPO=your-notes-repo
|
|
604
|
+
GITHUB_BRANCH=main
|
|
605
|
+
|
|
606
|
+
# Storage Configuration
|
|
607
|
+
R2_ACCOUNT_ID=your_account_id
|
|
608
|
+
R2_ACCESS_KEY_ID=your_access_key_id
|
|
609
|
+
R2_SECRET_ACCESS_KEY=your_secret_access_key
|
|
610
|
+
R2_BUCKET_NAME=your_bucket_name
|
|
611
|
+
R2_PUBLIC_URL=https://pub-xxxxxx.r2.dev
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## Troubleshooting
|
|
615
|
+
|
|
616
|
+
### Database Connection Issues
|
|
617
|
+
|
|
618
|
+
**Error: "connect ETIMEDOUT"**
|
|
619
|
+
- For Supabase: Use Connection Pooling URL (port 6543), not Direct Connection
|
|
620
|
+
- Increase `connectionTimeout` in database config
|
|
621
|
+
- Check firewall settings
|
|
622
|
+
|
|
623
|
+
**Error: "column does not exist"**
|
|
624
|
+
- The schema has automatic migrations
|
|
625
|
+
- Restart your app with `autoInitSchema: true`
|
|
626
|
+
- See [MIGRATION.md](MIGRATION.md) for manual migration steps
|
|
627
|
+
|
|
628
|
+
**Error: "UNSAFE_TRANSACTION"**
|
|
629
|
+
- This is handled automatically by the `postgres` package
|
|
630
|
+
- Ensure you're using the latest version
|
|
631
|
+
|
|
632
|
+
### Import/Module Issues
|
|
633
|
+
|
|
634
|
+
**Error: "Cannot use import statement outside a module"**
|
|
635
|
+
- Add `"type": "module"` to your package.json
|
|
636
|
+
- This package is ESM-only
|
|
170
637
|
|
|
171
638
|
## Contributing
|
|
172
639
|
|
|
173
640
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
174
641
|
|
|
642
|
+
1. Fork the repository
|
|
643
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
644
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
645
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
646
|
+
5. Open a Pull Request
|
|
647
|
+
|
|
175
648
|
## License
|
|
176
649
|
|
|
177
650
|
MIT License - see [LICENSE](LICENSE) for details
|
|
178
651
|
|
|
179
652
|
## Support
|
|
180
653
|
|
|
181
|
-
- 📖 [Documentation](lib/README.md)
|
|
182
|
-
- 💬 [Issues](https://github.com/cdthomp1/notes/issues)
|
|
183
|
-
- 📧 [
|
|
654
|
+
- 📖 [Full Documentation](lib/README.md)
|
|
655
|
+
- 💬 [GitHub Issues](https://github.com/cdthomp1/markdown-notes-engine/issues)
|
|
656
|
+
- 📧 [Supabase Setup Guide](docs/SUPABASE.md)
|
|
657
|
+
- 🔄 [Migration Guide](MIGRATION.md)
|
|
184
658
|
|
|
185
659
|
## Acknowledgments
|
|
186
660
|
|
|
@@ -188,9 +662,23 @@ Built with:
|
|
|
188
662
|
- [Express](https://expressjs.com/) - Web framework
|
|
189
663
|
- [Marked](https://marked.js.org/) - Markdown parser
|
|
190
664
|
- [Highlight.js](https://highlightjs.org/) - Syntax highlighting
|
|
191
|
-
- [
|
|
665
|
+
- [PostgreSQL](https://www.postgresql.org/) - Database and version control
|
|
666
|
+
- [postgres](https://github.com/porsager/postgres) - PostgreSQL client for Node.js
|
|
667
|
+
- [Octokit](https://github.com/octokit/rest.js/) - GitHub API client (optional)
|
|
192
668
|
- [AWS SDK](https://aws.amazon.com/sdk-for-javascript/) - S3/R2 client
|
|
193
669
|
|
|
670
|
+
## Changelog
|
|
671
|
+
|
|
672
|
+
### v1.0.1
|
|
673
|
+
- Migrated to ESM-only package
|
|
674
|
+
- Added PostgreSQL version control with Git-like architecture
|
|
675
|
+
- Added file history endpoint
|
|
676
|
+
- Improved database connection handling with `postgres` package
|
|
677
|
+
- Added support for Supabase and other remote PostgreSQL databases
|
|
678
|
+
- Added automatic schema migrations
|
|
679
|
+
- Flexible DELETE endpoint (supports query params or body)
|
|
680
|
+
- Built-in JSON body parser middleware
|
|
681
|
+
|
|
194
682
|
---
|
|
195
683
|
|
|
196
684
|
Made with ❤️ for markdown lovers everywhere
|