docs: update README, clean up old files

This commit is contained in:
lotherk
2026-03-27 13:56:47 +00:00
parent f6c4da1da3
commit 5b51de1214
7 changed files with 34 additions and 716 deletions

View File

@@ -1,46 +0,0 @@
# Multi-stage build: Backend + Frontend
FROM oven/bun:1.1-alpine AS backend-builder
WORKDIR /app/backend
COPY backend/package*.json ./
RUN bun install
COPY backend/prisma ./prisma
RUN bunx prisma generate
COPY backend/src ./src
RUN bun build src/index.ts --outdir ./dist --target bun
FROM node:20-alpine AS frontend-builder
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm install
COPY frontend ./
RUN npm run build
FROM oven/bun:1.1-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs || true
# Install nginx for serving frontend
RUN apk add --no-cache nginx
# Copy backend
COPY --from=backend-builder /app/backend/dist ./dist
COPY --from=backend-builder /app/backend/node_modules ./node_modules
COPY backend/package.json .
COPY backend/prisma ./prisma
# Copy frontend build
COPY --from=frontend-builder /app/frontend/dist ./public
# Setup nginx
COPY nginx.conf /etc/nginx/http.d/default.conf
RUN mkdir -p /data /run /app/nginx_tmp /var/lib/nginx/logs && chmod 777 /var/lib/nginx/logs /var/lib/nginx/tmp && chown -R bun:nodejs /data /app
# Start everything as root
CMD sh -c "nginx -g 'daemon off;' & bunx prisma db push --accept-data-loss & bun ./dist/index.js"

View File

@@ -1,444 +0,0 @@
# TotalRecall - AI-Powered Daily Journal
> Your day, analyzed. A journal that writes itself.
---
## Concept
TotalRecall is a privacy-first, self-hostable daily journal application that captures life through multiple input methods (text, photos, voice, data) and uses AI to generate thoughtful, reflective journal entries at the end of each day.
The core philosophy: **your data is yours**. Whether you host it yourself or use a hosted service, you own everything. The same codebase runs everywhere.
---
## The Problem
- Most journaling apps are siloed, paid, or data-mining platforms
- Manual journaling is time-consuming and inconsistent
- People capture moments (photos, voice memos, scattered notes) but never reflect on them
- AI tools exist but require manual input of context
## The Solution
TotalRecall aggregates all your daily inputs throughout the day, then at your command (or scheduled), sends everything to an AI to synthesize your day into a coherent, reflective journal entry.
---
## Features
### Input Capture
| Type | Description | Storage |
|------|-------------|---------|
| **Text Notes** | Quick thoughts, feelings, observations | Markdown |
| **Photos** | Camera or gallery uploads | JPEG/PNG in media folder |
| **Voice Memos** | Audio recordings | WebM/Opus |
| **Location** | Optional GPS tagging of entries | Lat/long metadata |
| **Health Data** | Manual entry (steps, mood, sleep) | JSON metadata |
### AI Journal Generation
- Aggregates all entries for a day
- Sends to configurable AI provider
- Generates reflective, narrative journal entry
- Stores generated journal alongside raw entries
### AI Provider Support (Pluggable)
- **OpenAI** - GPT-4, GPT-4-Turbo, GPT-3.5
- **Anthropic** - Claude 3 Opus, Sonnet, Haiku
- **Ollama** - Local LLM (Llama, Mistral, etc.)
- **LM Studio** - Local LLM with OpenAI-compatible API
---
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ End User │
│ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Web App │ │ Mobile App │ │
│ │ (React/PWA) │ │ (Future: iOS) │ │
│ └─────────┬──────────┘ └─────────┬──────────┘ │
│ │ │ │
│ └──────────┬─────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ REST API │ │
│ │ /api/v1/* │ │
│ │ │ │
│ │ - Authentication │ │
│ │ - Entries CRUD │ │
│ │ - Journal Gen │ │
│ │ - Settings │ │
│ └──────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ SQLite Database │ │
│ │ + Media Storage │ │
│ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Tech Stack
| Layer | Technology | Reason |
|-------|------------|--------|
| Backend Runtime | **Bun** | Fast startup, native TypeScript, small Docker image |
| API Framework | **Hono** | Lightweight, fast, works everywhere |
| Database | **SQLite / MySQL / PostgreSQL** (via Prisma ORM) | Switch databases with one line, zero-config for SQLite |
| Frontend | **React + Vite** | Fast dev, PWA capable |
| Styling | **Tailwind CSS** | Rapid UI development |
| Container | **Docker + Docker Compose** | One-command deployment |
---
## Deployment Modes
TotalRecall runs the same codebase whether self-hosted or on a SaaS platform.
### Single-User (Self-Hosted)
```yaml
services:
totalrecall:
image: ghcr.io/totalrecall/totalrecall:latest
ports:
- "3000:3000"
volumes:
- ./data:/data
```
- Users create accounts locally
- No external dependencies
- One Docker command to run
### Multi-Tenant (SaaS)
```yaml
services:
totalrecall:
image: ghcr.io/totalrecall/totalrecall:latest
ports:
- "3000:3000"
environment:
- DEPLOYMENT_MODE=multi-tenant
- JWT_SECRET=${JWT_SECRET}
- DATABASE_URL=file:/data/totalrecall.db
volumes:
- ./data:/data
```
- Multiple users with accounts
- API key authentication
- Same features as self-hosted
---
## API Design
### Authentication
```
Authorization: Bearer {api_key}
```
All requests (except auth endpoints) require a valid API key.
### Endpoints
```
POST /api/v1/auth/register # Create account
POST /api/v1/auth/login # Get API key
POST /api/v1/auth/logout # Invalidate session
GET /api/v1/days # List days with entries
GET /api/v1/days/:date # Get day's data (entries + journal)
DELETE /api/v1/days/:date # Delete day and all entries
POST /api/v1/entries # Create entry
GET /api/v1/entries/:id # Get entry
PUT /api/v1/entries/:id # Update entry
DELETE /api/v1/entries/:id # Delete entry
POST /api/v1/entries/:id/photo # Upload photo to entry
POST /api/v1/entries/:id/voice # Upload voice to entry
POST /api/v1/journal/generate/:date # Generate AI journal for date
GET /api/v1/journal/:date # Get generated journal
GET /api/v1/settings # Get user settings
PUT /api/v1/settings # Update settings (AI provider, etc.)
GET /api/v1/health # Health check (no auth)
```
### Response Format
```typescript
// Success
{ "data": { ... }, "error": null }
// Error
{ "data": null, "error": { "code": "NOT_FOUND", "message": "Entry not found" } }
```
---
## Data Models
### User
```typescript
interface User {
id: string; // UUID
email: string; // Unique
passwordHash: string; // bcrypt
createdAt: string; // ISO timestamp
}
```
### API Key
```typescript
interface ApiKey {
id: string;
userId: string;
keyHash: string; // SHA-256 hash, not stored plaintext
name: string; // "iPhone", "Web", etc.
lastUsedAt: string;
createdAt: string;
}
```
### Entry
```typescript
type EntryType = 'text' | 'voice' | 'photo' | 'health' | 'location';
interface Entry {
id: string;
userId: string;
date: string; // YYYY-MM-DD
type: EntryType;
content: string; // Text or metadata JSON
mediaPath?: string; // Path to uploaded file
metadata?: {
source?: 'manual' | 'health' | 'calendar';
location?: { lat: number; lng: number };
duration?: number; // For voice entries
[key: string]: unknown;
};
createdAt: string;
}
```
### Journal
```typescript
interface Journal {
id: string;
userId: string;
date: string; // YYYY-MM-DD
content: string; // Markdown
entryCount: number; // How many entries were used
generatedAt: string;
}
```
### Settings
```typescript
interface Settings {
userId: string;
aiProvider: 'openai' | 'anthropic' | 'ollama' | 'lmstudio';
aiConfig: {
// Provider-specific config (API keys stored encrypted)
model?: string;
baseUrl?: string; // For Ollama/LM Studio
};
journalPrompt?: string;
language: string;
}
```
---
## File Storage
```
/data/
├── totalrecall.db # SQLite database
└── media/
└── {user_id}/
└── {date}/
├── entry-{uuid}.webm # Voice recordings
└── entry-{uuid}.jpg # Photos
```
---
## Project Structure
```
totalrecall/
├── backend/
│ ├── src/
│ │ ├── index.ts # Entry point
│ │ ├── app.ts # Hono app setup
│ │ ├── routes/
│ │ │ ├── auth.ts # Registration, login
│ │ │ ├── days.ts # Day operations
│ │ │ ├── entries.ts # Entry CRUD
│ │ │ ├── journal.ts # Journal generation
│ │ │ └── settings.ts # User settings
│ │ ├── services/
│ │ │ ├── db.ts # Database connection
│ │ │ ├── storage.ts # File storage operations
│ │ │ └── ai/
│ │ │ ├── mod.ts # Provider interface
│ │ │ ├── openai.ts
│ │ │ ├── anthropic.ts
│ │ │ └── ollama.ts
│ │ ├── middleware/
│ │ │ └── auth.ts # API key validation
│ │ └── types.ts
│ ├── prisma/
│ │ └── schema.prisma # Database schema
│ ├── Dockerfile
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── main.tsx
│ │ ├── App.tsx
│ │ ├── pages/
│ │ │ ├── Home.tsx # Today's view
│ │ │ ├── History.tsx # Browse past days
│ │ │ ├── Journal.tsx # View generated journals
│ │ │ └── Settings.tsx # Configuration
│ │ ├── components/
│ │ │ ├── EntryInput.tsx
│ │ │ ├── PhotoCapture.tsx
│ │ │ ├── VoiceRecorder.tsx
│ │ │ ├── EntryList.tsx
│ │ │ └── JournalView.tsx
│ │ ├── lib/
│ │ │ └── api.ts # API client
│ │ └── hooks/
│ ├── Dockerfile
│ └── package.json
├── docker-compose.yml # Full stack
├── docker-compose.prod.yml # Production overrides
├── Dockerfile # Multi-stage build
└── README.md
```
---
## Implementation Phases
### Phase 1: Backend Foundation ✅
- [x] Bun + Hono setup
- [x] Prisma ORM with SQLite (dev) / PostgreSQL (prod)
- [x] User registration & login (JWT)
- [x] API key authentication
- [x] Basic CRUD routes
### Phase 2: Data Management ✅
- [x] Entry CRUD with media support
- [x] File upload handling (photos, voice)
- [x] Day aggregation queries
- [x] Media file storage
### Phase 3: AI Integration ✅
- [x] AI provider interface
- [x] OpenAI implementation
- [x] Anthropic implementation
- [x] Ollama implementation
- [x] Journal generation endpoint
### Phase 4: Frontend Core ✅
- [x] React + Vite setup
- [x] API client library
- [x] Authentication flow
- [x] Home page (today's entries)
- [x] Entry creation (text input)
### Phase 5: Media Inputs ✅
- [x] Photo capture/upload
- [x] Voice recording
- [x] Entry list with media preview
### Phase 6: Journal & Settings ✅
- [x] Journal viewing
- [x] Settings page
- [x] AI provider configuration
- [x] Prompt customization
### Phase 7: Deployment ✅
- [x] Docker multi-stage build
- [x] Docker Compose setup
- [x] Health checks
- [ ] PWA manifest
### Phase 8: Future
- [ ] iOS app (SwiftUI)
- [ ] Android app (Kotlin)
- [ ] Calendar integration
- [ ] Health data sync
---
## Security Considerations
- API keys hashed with SHA-256 (never stored plaintext)
- Passwords hashed with bcrypt
- CORS configurable for domain restriction
- Rate limiting on auth endpoints
- File upload validation (type, size)
- SQL injection prevention via ORM
---
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `file:./data/totalrecall.db` | SQLite/PostgreSQL/MySQL connection |
| `MEDIA_DIR` | `./data/media` | Directory for uploaded files |
| `JWT_SECRET` | (required) | Secret for JWT signing |
| `PORT` | `3000` | Server port |
| `CORS_ORIGIN` | `*` | Allowed origins |
| `RATE_LIMIT` | `100/hour` | Auth endpoint rate limit |
### Database Connection Examples
```bash
# SQLite (development, default)
DATABASE_URL="file:./data/totalrecall.db"
# PostgreSQL (production)
DATABASE_URL="postgresql://user:password@localhost:5432/totalrecall"
# MySQL
DATABASE_URL="mysql://user:password@localhost:3306/totalrecall"
```
---
## Licensing
This project is open source under [MIT License](LICENSE). You are free to:
- Use it for yourself
- Host it for others
- Modify and redistribute
No attribution required (but appreciated).

View File

@@ -2,8 +2,6 @@
Self-hosted AI-powered daily journaling application. Capture events throughout the day and let AI generate thoughtful diary pages from your entries. Self-hosted AI-powered daily journaling application. Capture events throughout the day and let AI generate thoughtful diary pages from your entries.
[![Deploy to Docker](https://img.shields.io/badge/Deploy-Docker-blue)](https://github.com/lotherk/deardiary)
## Features ## Features
- Quick event capture with keyboard shortcut (Ctrl+J) - Quick event capture with keyboard shortcut (Ctrl+J)
@@ -25,33 +23,47 @@ Self-hosted AI-powered daily journaling application. Capture events throughout t
```bash ```bash
# Clone the repository # Clone the repository
git clone https://github.com/lotherk/deardiary.git git clone git@git.kropa.tech:lotherk/deardiary.git
cd deardiary cd deardiary
# Configure environment (optional - defaults work out of box)
cp .env.example .env
# Start with Docker # Start with Docker
docker compose up -d docker compose up -d
``` ```
Access the app at `http://localhost:5173` Access the app at `http://localhost:3000`
Default credentials: `admin@localhost` / `changeme123` Default credentials: `admin@localhost` / `changeme123`
## Documentation
Documentation is included in the `www/` directory. Run the website container:
```bash
docker compose up -d docs
```
Access at `http://localhost:4000`
## Configuration ## Configuration
1. Go to Settings and select your AI provider (Groq, OpenAI, Anthropic, Ollama, LM Studio) ### Environment Variables
2. Enter your API key for the selected provider
3. Optionally customize the model and base URL Copy `.env.example` to `.env` and configure:
4. Test the connection before saving
```bash
# Backend
BACKEND_JWT_SECRET=your-secret-key
BACKEND_DEFAULT_USER_EMAIL=admin@example.com
BACKEND_DEFAULT_USER_PASSWORD=your-password
# Default AI for new users (optional)
BACKEND_DEFAULT_AI_PROVIDER=groq
BACKEND_DEFAULT_AI_MODEL=llama-3.3-70b-versatile
BACKEND_DEFAULT_AI_API_KEY=your-api-key
# Website links (for product website)
WEBSITE_APP_URL=https://your-app.example.com
GIT_URL=https://git.kropa.tech/lotherk/deardiary
```
### AI Providers
Go to Settings and select your AI provider (Groq, OpenAI, Anthropic, Ollama, LM Studio, xAI, Custom).
New users will use the system default AI settings by default. Users can uncheck "Use system default settings" to configure their own.
## Development ## Development
@@ -96,9 +108,10 @@ docker compose build && docker compose up -d
│ ├── components/ │ ├── components/
│ └── lib/ # API client, geolocation │ └── lib/ # API client, geolocation
├── www/ # Product website with docs ├── www/ # Product website with docs
├── Dockerfile # App container ├── Dockerfile # App container (frontend + backend + nginx)
├── Dockerfile.docs # Website container ├── Dockerfile.website # Website container with envsubst
── docker-compose.yml ── docker-compose.yml
└── docker-entrypoint.d/ # Entrypoint scripts
``` ```
## Routes ## Routes

View File

@@ -1,130 +0,0 @@
# TotalRecall
> Your day, analyzed. A journal that writes itself.
AI-powered daily journal that captures life through multiple input methods and generates thoughtful, reflective journal entries.
## Features
- **Multiple Input Types**: Text notes, photos, voice memos, health data
- **AI Journal Generation**: OpenAI, Anthropic, Ollama, or LM Studio
- **Self-Hostable**: Run it yourself or use any hosted version
- **Same Codebase**: Single deployment works for single-user or multi-tenant
## Quick Start
### Docker (Recommended)
```bash
git clone https://github.com/totalrecall/totalrecall.git
cd totalrecall
# Create .env file
cp backend/.env.example .env
# Edit .env and set JWT_SECRET
# Start
docker-compose up -d
```
Visit http://localhost:5173
### Manual Development
```bash
# Backend
cd backend
bun install
bunx prisma generate
bunx prisma db push
bun run dev
# Frontend (separate terminal)
cd frontend
npm install
npm run dev
```
## Configuration
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `file:./data/totalrecall.db` | SQLite, PostgreSQL, or MySQL |
| `JWT_SECRET` | (required) | Secret for JWT signing |
| `MEDIA_DIR` | `./data/media` | Directory for uploads |
| `PORT` | `3000` | Server port |
| `CORS_ORIGIN` | `*` | Allowed origins |
### Database Examples
```bash
# SQLite (default)
DATABASE_URL="file:./data/totalrecall.db"
# PostgreSQL
DATABASE_URL="postgresql://user:pass@host:5432/totalrecall"
# MySQL
DATABASE_URL="mysql://user:pass@host:3306/totalrecall"
```
## AI Providers
Configure in Settings after logging in:
| Provider | Setup |
|----------|-------|
| **OpenAI** | API key required |
| **Anthropic** | API key required |
| **Ollama** | Local URL (default: http://localhost:11434) |
| **LM Studio** | Local URL (default: http://localhost:1234/v1) |
## Project Structure
```
totalrecall/
├── backend/ # Bun + Hono API server
│ ├── src/
│ │ ├── routes/ # API endpoints
│ │ ├── services/ # AI providers
│ │ └── middleware/
│ └── prisma/ # Database schema
├── frontend/ # React + Vite web app
├── android/ # Native Android app (Kotlin + Compose)
│ └── app/src/main/java/com/totalrecall/
├── docker-compose.yml
└── PLAN.md # Full specification
```
## API
All endpoints require `Authorization: Bearer {api_key}` header.
```
POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/auth/api-key
GET /api/v1/days
GET /api/v1/days/:date
DELETE /api/v1/days/:date
POST /api/v1/entries
GET /api/v1/entries/:id
PUT /api/v1/entries/:id
DELETE /api/v1/entries/:id
POST /api/v1/entries/:id/photo
POST /api/v1/entries/:id/voice
POST /api/v1/journal/generate/:date
GET /api/v1/journal/:date
GET /api/v1/settings
PUT /api/v1/settings
```
## License
MIT

View File

@@ -1,30 +0,0 @@
version: '3.8'
services:
backend:
image: ghcr.io/totalrecall/totalrecall:latest
ports:
- "3000:3000"
environment:
- DATABASE_URL=file:/data/totalrecall.db
- MEDIA_DIR=/data/media
- JWT_SECRET=${JWT_SECRET}
- PORT=3000
- CORS_ORIGIN=https://your-domain.com
volumes:
- ./data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
# Uncomment to use with a reverse proxy like Traefik
# frontend:
# image: ghcr.io/totalrecall/frontend:latest
# labels:
# - "traefik.enable=true"
# - "traefik.http.routers.totalrecall.rule=Host(`your-domain.com`)"
# - "traefik.http.routers.totalrecall.entrypoints=websecure"
# - "traefik.http.routers.totalrecall.tls=true"

View File

@@ -1,24 +0,0 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
- "5173:80"
environment:
- DATABASE_URL=file:/data/totalrecall.db
- MEDIA_DIR=/data/media
- JWT_SECRET=${JWT_SECRET:-change-me-in-production}
- PORT=3000
- CORS_ORIGIN=${CORS_ORIGIN:-*}
volumes:
- ./data:/data
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3

View File

@@ -1,21 +0,0 @@
server {
listen 80;
root /app/public;
index index.html;
client_body_temp_path /app/nginx_tmp/client_body;
proxy_temp_path /app/nginx_tmp/proxy;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}