added encryption
This commit is contained in:
@@ -39,15 +39,15 @@ Stores user profile information. One document per unique email.
|
||||
|
||||
#### Field Descriptions
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
| ----------- | ------ | -------- | ----------------------------------------- |
|
||||
| `_id` | ObjectId | Yes | Unique primary key, auto-generated |
|
||||
| `email` | String | Yes | User's email; unique constraint; indexed |
|
||||
| `displayName` | String | Yes | User's display name (from Google Auth) |
|
||||
| `photoURL` | String | No | User's profile photo URL |
|
||||
| `theme` | String | Yes | Theme preference: "light" or "dark" |
|
||||
| `createdAt` | Date | Yes | Account creation timestamp |
|
||||
| `updatedAt` | Date | Yes | Last profile update timestamp |
|
||||
| Field | Type | Required | Notes |
|
||||
| ------------- | -------- | -------- | ---------------------------------------- |
|
||||
| `_id` | ObjectId | Yes | Unique primary key, auto-generated |
|
||||
| `email` | String | Yes | User's email; unique constraint; indexed |
|
||||
| `displayName` | String | Yes | User's display name (from Google Auth) |
|
||||
| `photoURL` | String | No | User's profile photo URL |
|
||||
| `theme` | String | Yes | Theme preference: "light" or "dark" |
|
||||
| `createdAt` | Date | Yes | Account creation timestamp |
|
||||
| `updatedAt` | Date | Yes | Last profile update timestamp |
|
||||
|
||||
#### Unique Constraints
|
||||
|
||||
@@ -84,11 +84,11 @@ Stores journal entries for each user. Each entry has a logical journal date and
|
||||
mood: "happy" | "sad" | "neutral" | "anxious" | "grateful" | null,
|
||||
tags: string[],
|
||||
isPublic: boolean,
|
||||
|
||||
|
||||
entryDate: Date, // Logical journal date
|
||||
createdAt: Date,
|
||||
updatedAt: Date,
|
||||
|
||||
|
||||
encryption: {
|
||||
encrypted: boolean,
|
||||
iv: string | null, // Base64-encoded initialization vector
|
||||
@@ -99,19 +99,19 @@ Stores journal entries for each user. Each entry has a logical journal date and
|
||||
|
||||
#### Field Descriptions
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
| ---------- | ------ | -------- | ------------------------------------------ |
|
||||
| `_id` | ObjectId | Yes | Entry ID; auto-generated; indexed |
|
||||
| `userId` | ObjectId | Yes | Reference to user._id; indexed; enforced |
|
||||
| `title` | String | Yes | Entry title/headline |
|
||||
| `content` | String | Yes | Entry body content |
|
||||
| `mood` | String | No | Mood selector (null if not set) |
|
||||
| `tags` | Array | Yes | Array of user-defined tags [] |
|
||||
| `isPublic` | Bool | Yes | Public sharing flag (currently unused) |
|
||||
| `entryDate` | Date | Yes | Logical journal date (start of day, UTC) |
|
||||
| `createdAt` | Date | Yes | Database write timestamp |
|
||||
| `updatedAt` | Date | Yes | Last modification timestamp |
|
||||
| `encryption` | Object | Yes | Encryption metadata (nested) |
|
||||
| Field | Type | Required | Notes |
|
||||
| ------------ | -------- | -------- | ----------------------------------------- |
|
||||
| `_id` | ObjectId | Yes | Entry ID; auto-generated; indexed |
|
||||
| `userId` | ObjectId | Yes | Reference to user.\_id; indexed; enforced |
|
||||
| `title` | String | Yes | Entry title/headline |
|
||||
| `content` | String | Yes | Entry body content |
|
||||
| `mood` | String | No | Mood selector (null if not set) |
|
||||
| `tags` | Array | Yes | Array of user-defined tags [] |
|
||||
| `isPublic` | Bool | Yes | Public sharing flag (currently unused) |
|
||||
| `entryDate` | Date | Yes | Logical journal date (start of day, UTC) |
|
||||
| `createdAt` | Date | Yes | Database write timestamp |
|
||||
| `updatedAt` | Date | Yes | Last modification timestamp |
|
||||
| `encryption` | Object | Yes | Encryption metadata (nested) |
|
||||
|
||||
#### Encryption Metadata
|
||||
|
||||
@@ -124,6 +124,7 @@ Stores journal entries for each user. Each entry has a logical journal date and
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- `encrypted: false` by default (plain text storage)
|
||||
- When setting `encrypted: true`, client provides `iv` and `algorithm`
|
||||
- Server stores metadata but does NOT decrypt; decryption happens client-side
|
||||
@@ -160,26 +161,26 @@ Indexes optimize query performance. All indexes are created by the `scripts/crea
|
||||
|
||||
```javascript
|
||||
// Unique index on email (prevents duplicates)
|
||||
db.users.createIndex({ email: 1 }, { unique: true })
|
||||
db.users.createIndex({ email: 1 }, { unique: true });
|
||||
|
||||
// For sorting users by creation date
|
||||
db.users.createIndex({ createdAt: -1 })
|
||||
db.users.createIndex({ createdAt: -1 });
|
||||
```
|
||||
|
||||
### Entries Indexes
|
||||
|
||||
```javascript
|
||||
// Compound index for history pagination (most recent first)
|
||||
db.entries.createIndex({ userId: 1, createdAt: -1 })
|
||||
db.entries.createIndex({ userId: 1, createdAt: -1 });
|
||||
|
||||
// Compound index for calendar queries by date
|
||||
db.entries.createIndex({ userId: 1, entryDate: 1 })
|
||||
db.entries.createIndex({ userId: 1, entryDate: 1 });
|
||||
|
||||
// For tag-based searches (future feature)
|
||||
db.entries.createIndex({ tags: 1 })
|
||||
db.entries.createIndex({ tags: 1 });
|
||||
|
||||
// For sorting by entry date
|
||||
db.entries.createIndex({ entryDate: -1 })
|
||||
db.entries.createIndex({ entryDate: -1 });
|
||||
```
|
||||
|
||||
### Index Rationale
|
||||
@@ -385,15 +386,15 @@ iso_string = dt.isoformat()
|
||||
|
||||
### What Changed
|
||||
|
||||
| Aspect | Old Schema | New Schema |
|
||||
| -------------- | ------------------------- | ------------------------------- |
|
||||
| Users | Many per email possible | One per email (unique) |
|
||||
| User _id | ObjectId (correct) | ObjectId (unchanged) |
|
||||
| Entry userId | String | ObjectId |
|
||||
| Entry date | Only `createdAt` | `createdAt` + `entryDate` |
|
||||
| Encryption | Not supported | Metadata in `encryption` field |
|
||||
| Settings | Separate collection | Merged into `users.theme` |
|
||||
| Indexes | None | Comprehensive indexes |
|
||||
| Aspect | Old Schema | New Schema |
|
||||
| ------------ | ----------------------- | ------------------------------ |
|
||||
| Users | Many per email possible | One per email (unique) |
|
||||
| User \_id | ObjectId (correct) | ObjectId (unchanged) |
|
||||
| Entry userId | String | ObjectId |
|
||||
| Entry date | Only `createdAt` | `createdAt` + `entryDate` |
|
||||
| Encryption | Not supported | Metadata in `encryption` field |
|
||||
| Settings | Separate collection | Merged into `users.theme` |
|
||||
| Indexes | None | Comprehensive indexes |
|
||||
|
||||
### Migration Steps
|
||||
|
||||
@@ -471,7 +472,8 @@ mongorestore --db grateful_journal ./backup-entries
|
||||
|
||||
### Q: How do I encrypt entry content?
|
||||
|
||||
**A:**
|
||||
**A:**
|
||||
|
||||
1. Client encrypts content client-side using a key (not transmitted)
|
||||
2. Client sends encrypted content + metadata (iv, algorithm)
|
||||
3. Server stores content + encryption metadata as-is
|
||||
@@ -480,14 +482,17 @@ mongorestore --db grateful_journal ./backup-entries
|
||||
### Q: What if I have duplicate users?
|
||||
|
||||
**A:** Run the migration script:
|
||||
|
||||
```bash
|
||||
python backend/scripts/migrate_data.py
|
||||
```
|
||||
|
||||
It detects duplicates, keeps the oldest, and consolidates entries.
|
||||
|
||||
### Q: Should I paginate entries?
|
||||
|
||||
**A:** Yes. Use `skip` and `limit` to prevent loading thousands of entries:
|
||||
|
||||
```
|
||||
GET /api/entries/{user_id}?skip=0&limit=50
|
||||
```
|
||||
@@ -495,6 +500,7 @@ GET /api/entries/{user_id}?skip=0&limit=50
|
||||
### Q: How do I query entries by date range?
|
||||
|
||||
**A:** Use the calendar endpoint or build a query:
|
||||
|
||||
```python
|
||||
db.entries.find({
|
||||
"userId": oid,
|
||||
|
||||
Reference in New Issue
Block a user