Add Bun test suite covering: - Auth: register, login, duplicate email, invalid password - Events: create, location, validation, unauthorized - Journals: get, generate, delete, list with pagination - Settings: get, update, clear prompt, change password - Export/Import: export data, events with location - Search: search events, no matches, cross-date - Days: list days, get day details, empty day
210 lines
6.8 KiB
Markdown
210 lines
6.8 KiB
Markdown
# DearDiary - AI-Powered Daily Journal
|
|
|
|
Self-hosted journaling app where users capture events throughout the day and AI generates diary pages. Events become immutable once a diary is generated (locked).
|
|
|
|
## Tech Stack
|
|
|
|
- **Backend**: Bun + Hono + Prisma (SQLite)
|
|
- **Frontend**: React + Vite + TypeScript + Tailwind CSS
|
|
- **Database**: SQLite (file-based)
|
|
- **Deployment**: Docker
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
/backend
|
|
/prisma
|
|
schema.prisma # Database schema (models: User, Event, Journal, Task, Settings)
|
|
/src
|
|
index.ts # Main API routes, journal generation logic
|
|
/services/ai # AI provider implementations (Groq, OpenAI, Anthropic, Ollama, LMStudio)
|
|
/__tests__/ # API unit tests (Bun test)
|
|
|
|
/frontend
|
|
/src
|
|
/pages # Page components (Dashboard, Home, Journal, Diary, Settings, Day, Tasks, Calendar)
|
|
/components # Reusable components (QuickAddWidget, SearchModal, DateNavigator, EntryList, etc.)
|
|
/lib
|
|
api.ts # API client with typed methods
|
|
geolocation.ts # Browser geolocation with reverse geocoding
|
|
ThemeContext.tsx # Dark/light theme
|
|
```
|
|
|
|
## Key Concepts
|
|
|
|
### Event vs Journal
|
|
- **Event**: User input during the day (immutable after journal is generated)
|
|
- **Journal**: AI-generated diary page from events (can regenerate, title + content)
|
|
|
|
### Terminology
|
|
- "Diary Page" not "Journal"
|
|
- "Event" not "Entry"
|
|
- "Generate" not "Create"
|
|
- "Rewrite" not "Regenerate"
|
|
- "Today" is the event stream page (`/today`)
|
|
|
|
### Routes
|
|
- `/` - Dashboard (recent diary pages with excerpts)
|
|
- `/today` - Today's event stream (main capture page)
|
|
- `/diary` - Paginated diary reader (10/50/100 per page)
|
|
- `/journal/:date` - View/edit diary page with generation tasks
|
|
- `/day/:date` - View day's events with DateNavigator
|
|
- `/settings` - Configuration
|
|
- `/calendar` - Month calendar view
|
|
|
|
## Database Schema
|
|
|
|
### Journal Model (key fields)
|
|
```
|
|
id, userId, date, title, content, eventCount, generatedAt
|
|
```
|
|
|
|
### Task Model
|
|
Stores generation attempts with full request/response JSON for debugging.
|
|
```
|
|
id, userId, journalId, type, status, provider, model, prompt, request, response, error, title, createdAt, completedAt
|
|
```
|
|
|
|
### Event Model (key fields)
|
|
```
|
|
id, userId, date, type, content, mediaPath, metadata, latitude, longitude, placeName, createdAt
|
|
```
|
|
- Location is captured automatically from browser geolocation when creating events
|
|
- Reverse geocoding via OpenStreetMap Nominatim API provides place names
|
|
|
|
## API Design
|
|
|
|
All endpoints return: `{ data: T | null, error: { code, message } | null }`
|
|
|
|
### Authentication
|
|
- API key in `Authorization: Bearer <key>` header
|
|
- Keys stored as SHA-256 hashes
|
|
|
|
### Key Endpoints
|
|
```
|
|
POST /api/v1/journal/generate/:date # Generate diary (with optional instructions)
|
|
GET /api/v1/journal/:date # Get diary page
|
|
DELETE /api/v1/journal/:date # Delete to unlock events
|
|
GET /api/v1/journal/:date/tasks # Generation tasks (includes title per task)
|
|
GET /api/v1/journals # List journals with pagination (?page=1&limit=10)
|
|
GET /api/v1/days # List days with journal info (includes excerpt)
|
|
POST /api/v1/events # Create event
|
|
GET /api/v1/export # Export all user data (JSON)
|
|
POST /api/v1/import # Import data (with version checking)
|
|
```
|
|
|
|
## Export/Import
|
|
|
|
### Export Format
|
|
Exports include:
|
|
- `version`: DearDiary version string (e.g., "0.1.0")
|
|
- `exportedAt`: ISO timestamp of export
|
|
- `settings`: User settings including AI provider configuration
|
|
- `events`: All user events (includes latitude, longitude, placeName)
|
|
- `journals`: All generated diary pages
|
|
- `tasks`: All generation tasks
|
|
|
|
### Version Compatibility
|
|
- Minimum supported import version: 0.0.3
|
|
- Import validates version compatibility
|
|
- Warns if importing older/newer version
|
|
- Older exports may fail or lose data
|
|
|
|
### Import Behavior
|
|
- Duplicates are skipped (based on date + content + timestamp for events)
|
|
- Journals matched by date
|
|
- Tasks linked to journals by date
|
|
- Settings are overwritten
|
|
|
|
## AI Integration
|
|
|
|
### Providers
|
|
- Groq (default, uses llama-3.3-70b-versatile)
|
|
- OpenAI
|
|
- Anthropic
|
|
- Ollama (local)
|
|
- LM Studio (local)
|
|
|
|
### Prompt System
|
|
- Default system prompt is defined in `backend/src/index.ts` (hardcoded, not user-configurable)
|
|
- User can add custom instructions via `settings.journalPrompt` field (labeled "Prompt" in UI)
|
|
- Custom instructions are prepended to the default prompt when set
|
|
- Default prompt includes anti-hallucination rules and structure guidelines
|
|
|
|
### JSON Mode
|
|
AI is configured to return JSON with `response_format: { type: "json_object" }` where supported. Journal generation prompts instruct AI to return:
|
|
```json
|
|
{"title": "Short title", "content": "Diary text..."}
|
|
```
|
|
|
|
### Provider Settings Storage
|
|
Settings stored as `providerSettings: { "groq": { apiKey, model, baseUrl }, ... }` with `aiProvider` determining which is active.
|
|
|
|
## Coding Guidelines
|
|
|
|
### TypeScript
|
|
- Use explicit interfaces for API responses
|
|
- Avoid `any` types
|
|
- Use optional chaining and nullish coalescing
|
|
|
|
### React Components
|
|
- Functional components with hooks
|
|
- Props interfaces defined at top of file
|
|
- Use `useState` for local state, `useEffect` for data loading
|
|
|
|
### Tailwind CSS
|
|
- Dark theme by default (slate color palette)
|
|
- Use `text-slate-400` for muted text
|
|
- Use `purple-*` for primary actions
|
|
- Use `slate-900` for cards/containers
|
|
|
|
### API Response Handling
|
|
```typescript
|
|
const res = await api.someMethod();
|
|
if (res.error) {
|
|
// handle error
|
|
} else if (res.data) {
|
|
// use res.data
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
- Backend returns `{ code, message }` errors
|
|
- Frontend displays errors inline or as toast notifications
|
|
- Generation errors shown in red banner
|
|
|
|
## Common Tasks
|
|
|
|
### Adding a new AI provider
|
|
1. Create `/backend/src/services/ai/<provider>.ts`
|
|
2. Implement `AIProvider` interface with `generate(prompt, systemPrompt, options?)`
|
|
3. Add to `provider.ts` `createAIProvider()` switch
|
|
4. Add `jsonMode` parsing if supported
|
|
|
|
### Database migrations
|
|
```bash
|
|
cd backend
|
|
bunx prisma migrate dev --name migration_name
|
|
```
|
|
|
|
### Docker rebuild
|
|
```bash
|
|
docker compose build && docker compose up -d
|
|
```
|
|
|
|
### Running Tests
|
|
```bash
|
|
cd backend
|
|
bun run test:server
|
|
```
|
|
Tests require the server running. The test script starts the server, runs tests, then stops it.
|
|
|
|
## Version History
|
|
- 0.1.0: Automatic geolocation capture, Starlight documentation site
|
|
- 0.0.6: Automatic geolocation capture on event creation, reverse geocoding to place names
|
|
- 0.0.5: Export/Import feature with version checking
|
|
- 0.0.4: /diary page with pagination (10/50/100), Task.title field, dashboard excerpts
|
|
- 0.0.3: AI returns JSON with title + content, UI shows titles
|
|
- 0.0.2: Dashboard, Quick Add widget (Ctrl+J), rewrite modal
|
|
- 0.0.1: Initial release with Entry->Event terminology fix
|