## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
672 lines
16 KiB
Markdown
672 lines
16 KiB
Markdown
# Architecture - keep-notes (Memento Web App)
|
|
|
|
## Overview
|
|
|
|
Complete system architecture for the Memento web application, a Next.js 16 full-stack application using the App Router architecture pattern.
|
|
|
|
**Architecture Pattern:** Full-stack JAMstack with Server-Side Rendering (SSR)
|
|
**Framework:** Next.js 16.1.1 (App Router)
|
|
**Language:** TypeScript 5
|
|
**Database:** SQLite via Prisma ORM
|
|
|
|
---
|
|
|
|
## Technology Stack
|
|
|
|
### Frontend
|
|
| Technology | Version | Purpose |
|
|
|------------|---------|---------|
|
|
| React | 19.2.3 | UI library |
|
|
| Next.js | 16.1.1 | Full-stack framework |
|
|
| TypeScript | 5.x | Type safety |
|
|
| Tailwind CSS | 4.x | Styling |
|
|
| Radix UI | Multiple | Component primitives |
|
|
| Lucide React | 0.562.0 | Icons |
|
|
|
|
### Backend (Integrated)
|
|
| Technology | Version | Purpose |
|
|
|------------|---------|---------|
|
|
| Next.js API Routes | Built-in | REST API |
|
|
| Prisma | 5.22.0 | ORM |
|
|
| better-sqlite3 | 12.5.0 | SQLite driver |
|
|
| @libsql/client | 0.15.15 | Alternative DB client |
|
|
| NextAuth | 5.0.0-beta.30 | Authentication |
|
|
|
|
### AI/LLM
|
|
| Technology | Version | Purpose |
|
|
|------------|---------|---------|
|
|
| Vercel AI SDK | 6.0.23 | AI integration |
|
|
| OpenAI Provider | 3.0.7 | GPT models |
|
|
| Ollama Provider | 1.2.0 | Local models |
|
|
|
|
### Additional
|
|
- @dnd-kit (drag and drop)
|
|
- Muuri (masonry grid)
|
|
- react-markdown (markdown rendering)
|
|
- nodemailer (email)
|
|
- bcryptjs (password hashing)
|
|
- Zod (validation)
|
|
- Playwright (testing)
|
|
|
|
---
|
|
|
|
## Architecture Pattern: JAMstack with App Router
|
|
|
|
### Request Flow
|
|
|
|
```
|
|
User Browser
|
|
↓
|
|
Next.js App Router
|
|
↓
|
|
├─────────────────┬─────────────────┐
|
|
│ │ │
|
|
React Server API Routes Server Actions
|
|
Components (REST) (Mutations)
|
|
│ │ │
|
|
└─────────────────┴─────────────────┘
|
|
↓
|
|
Prisma ORM
|
|
↓
|
|
SQLite Database
|
|
```
|
|
|
|
### Rendering Strategy
|
|
- **Server Components:** Default (faster initial load, SEO friendly)
|
|
- **Client Components:** Interactive features (drag-drop, forms)
|
|
- **Streaming:** Progressive rendering with Suspense
|
|
- **ISR:** Not used (dynamic content)
|
|
|
|
---
|
|
|
|
## Directory Structure (App Router)
|
|
|
|
```
|
|
app/
|
|
├── (auth)/ # Auth route group
|
|
│ ├── layout.tsx # Auth layout
|
|
│ ├── login/page.tsx # Login page
|
|
│ ├── register/page.tsx # Register page
|
|
│ └── [reset flows]/ # Password reset
|
|
│
|
|
├── (main)/ # Main app route group
|
|
│ ├── layout.tsx # Main layout
|
|
│ ├── page.tsx # Home/dashboard
|
|
│ ├── admin/ # Admin panel
|
|
│ ├── archive/ # Archived notes
|
|
│ └── settings/ # User settings
|
|
│
|
|
├── actions/ # Server actions
|
|
│ ├── notes.ts # Note mutations
|
|
│ ├── register.ts # User registration
|
|
│ └── [other actions] # Additional mutations
|
|
│
|
|
├── api/ # REST API
|
|
│ ├── auth/[...nextauth]/ # NextAuth handler
|
|
│ ├── notes/ # Note CRUD
|
|
│ ├── labels/ # Label CRUD
|
|
│ ├── ai/ # AI endpoints
|
|
│ ├── admin/ # Admin endpoints
|
|
│ └── [other routes] # Additional endpoints
|
|
│
|
|
├── globals.css # Global styles
|
|
└── layout.tsx # Root layout
|
|
```
|
|
|
|
---
|
|
|
|
## Component Architecture
|
|
|
|
### Component Hierarchy
|
|
|
|
```
|
|
layout.tsx (Root)
|
|
├── HeaderWrapper
|
|
│ ├── Header
|
|
│ │ ├── Logo/Title
|
|
│ │ └── UserNav
|
|
│ └── [Auth providers]
|
|
│
|
|
├── Sidebar (collapsible)
|
|
│ ├── Navigation
|
|
│ └── Filters
|
|
│
|
|
└── Page Content
|
|
└── MasonryGrid
|
|
└── NoteCard[n]
|
|
├── NoteEditor
|
|
├── NoteChecklist
|
|
├── NoteImages
|
|
└── NoteActions
|
|
```
|
|
|
|
### State Management
|
|
|
|
**No Global State Library** (Redux, Zustand, etc.)
|
|
|
|
**State Strategies:**
|
|
1. **Server State:** Fetched from API, cached with React Cache
|
|
2. **URL State:** Search params, route params
|
|
3. **Form State:** Controlled components with useState
|
|
4. **Context:** User session, theme preference
|
|
5. **Server Actions:** Mutations that update DB
|
|
|
|
**Data Flow:**
|
|
```
|
|
User Action
|
|
↓
|
|
Server Action / API Call
|
|
↓
|
|
Prisma Mutation
|
|
↓
|
|
Database Update
|
|
↓
|
|
Revalidate / Refetch
|
|
↓
|
|
UI Update
|
|
```
|
|
|
|
---
|
|
|
|
## API Architecture
|
|
|
|
### REST Endpoints
|
|
|
|
**Base URL:** `/api`
|
|
|
|
**Authentication:** NextAuth session (most endpoints)
|
|
|
|
**Endpoints:**
|
|
|
|
| Method | Endpoint | Purpose | Auth |
|
|
|--------|----------|---------|------|
|
|
| GET | `/api/notes` | List notes | No (currently) |
|
|
| POST | `/api/notes` | Create note | No (currently) |
|
|
| PUT | `/api/notes` | Update note | No (currently) |
|
|
| DELETE | `/api/notes` | Delete note | No (currently) |
|
|
| GET | `/api/labels` | List labels | Yes |
|
|
| POST | `/api/labels` | Create label | Yes |
|
|
| DELETE | `/api/labels/{id}` | Delete label | Yes |
|
|
| GET/POST | `/api/auth/[...nextauth]` | Auth handler | No (this is auth) |
|
|
| POST | `/api/ai/tags` | Auto-tagging | TBD |
|
|
| POST | `/api/upload` | File upload | TBD |
|
|
| POST | `/api/admin/*` | Admin ops | Yes (admin) |
|
|
|
|
**Response Format:**
|
|
```json
|
|
{
|
|
"success": true|false,
|
|
"data": any,
|
|
"error": string // only when success: false
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Server Actions Architecture
|
|
|
|
**Location:** `app/actions/`
|
|
|
|
**Purpose:** Mutations that bypass REST, direct server-side execution
|
|
|
|
**Examples:**
|
|
- `notes.ts`: Create, update, delete notes
|
|
- `register.ts`: User registration
|
|
- `scrape.ts`: Web scraping for link previews
|
|
|
|
**Benefits:**
|
|
- Type-safe (from schema)
|
|
- No API layer needed
|
|
- Direct database access
|
|
- Form validation (Zod)
|
|
|
|
**Usage:**
|
|
```tsx
|
|
// Client component
|
|
import { createNote } from '@/app/actions/notes'
|
|
|
|
function NoteForm() {
|
|
async function handleSubmit(data) {
|
|
await createNote(data) // Server action
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Database Architecture
|
|
|
|
### ORM: Prisma
|
|
|
|
**Schema Location:** `prisma/schema.prisma`
|
|
**Migrations:** `prisma/migrations/` (13 migrations)
|
|
**Database File:** `prisma/dev.db`
|
|
|
|
**Models:**
|
|
- User (authentication, profile)
|
|
- Account (OAuth providers)
|
|
- Session (active sessions)
|
|
- VerificationToken (email verification)
|
|
- Note (core data model)
|
|
- Label (tags/categories)
|
|
- SystemConfig (key-value store)
|
|
|
|
**Connection:**
|
|
```typescript
|
|
// lib/prisma.ts
|
|
import { PrismaClient } from '@prisma/client'
|
|
import { PrismaLibSQL } from '@prisma/adapter-libsql'
|
|
|
|
const prisma = new PrismaClient()
|
|
```
|
|
|
|
**Adapters:**
|
|
- `@prisma/adapter-better-sqlite3` (primary - local dev)
|
|
- `@prisma/adapter-libsql` (alternative - Turso cloud)
|
|
|
|
---
|
|
|
|
## Authentication Architecture
|
|
|
|
### NextAuth.js v5
|
|
|
|
**Configuration:** `auth.config.ts`
|
|
**Implementation:** `auth.ts`
|
|
|
|
**Providers:**
|
|
- Credentials (email/password)
|
|
- OAuth options (Google, GitHub, etc.)
|
|
|
|
**Strategy:**
|
|
1. User submits credentials
|
|
2. NextAuth validates against database
|
|
3. Session created in `Session` table
|
|
4. JWT token issued
|
|
5. Session stored in HTTP-only cookie
|
|
|
|
**Session Management:**
|
|
- Server-side sessions in database
|
|
- HTTP-only cookies for security
|
|
- Automatic token refresh
|
|
|
|
**Password Security:**
|
|
- bcryptjs hashing (cost factor: default)
|
|
- Password reset flow with tokens
|
|
- Reset token stored in `User.resetToken`
|
|
|
|
**User Roles:**
|
|
- `USER` (default)
|
|
- `ADMIN` (elevated permissions)
|
|
- Role-based access control in API routes
|
|
|
|
---
|
|
|
|
## AI Integration Architecture
|
|
|
|
### Provider Pattern
|
|
|
|
**Location:** `lib/ai/providers/`
|
|
|
|
**Providers:**
|
|
- OpenAI (`ollama.ts` is misnamed, should be `openai.ts` or separate)
|
|
- Ollama (`ollama.ts` - local models)
|
|
|
|
**Factory Pattern:**
|
|
```typescript
|
|
// lib/ai/factory.ts
|
|
export function createProvider(provider: string) {
|
|
switch (provider) {
|
|
case 'openai': return new OpenAIProvider()
|
|
case 'ollama': return new OllamaProvider()
|
|
}
|
|
}
|
|
```
|
|
|
|
**Features:**
|
|
- Auto-tagging (suggest labels for notes)
|
|
- Semantic search (vector embeddings)
|
|
- Content summarization (future)
|
|
- Smart categorization (future)
|
|
|
|
**AI SDK Usage:**
|
|
```typescript
|
|
import { generateText } from 'ai'
|
|
import { openai } from '@ai-sdk/openai'
|
|
|
|
const response = await generateText({
|
|
model: openai('gpt-4'),
|
|
prompt: note.content
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Feature Architecture
|
|
|
|
### Note Management
|
|
|
|
**Data Flow:**
|
|
1. User creates note → `NoteInput` component
|
|
2. Server action → `app/actions/notes.ts`
|
|
3. Prisma create → `Note` table
|
|
4. Revalidate → UI updates
|
|
5. Real-time → No WebSocket currently
|
|
|
|
**Search:**
|
|
- Text search: SQL `LIKE` queries (case-insensitive)
|
|
- Semantic search: Vector embeddings (JSON field)
|
|
- Filtering: By labels, archived status, pinned status
|
|
|
|
**Organization:**
|
|
- Pinning: `isPinned` boolean
|
|
- Archiving: `isArchived` boolean
|
|
- Ordering: `order` field (drag-drop)
|
|
- Colors: `color` string
|
|
- Size: `size` (small, medium, large)
|
|
|
|
### Label System
|
|
|
|
**Two Approaches:**
|
|
1. **Label Table:** `Label` model with user ownership
|
|
2. **Note Labels:** JSON array in `Note.labels`
|
|
|
|
**Current State:** Both exist (migration artifact)
|
|
- `Label` table: User-managed labels
|
|
- `Note.labels`: JSON array of label names
|
|
|
|
**Future:** Consolidate to one approach
|
|
|
|
### Reminder System
|
|
|
|
**Fields:**
|
|
- `reminder`: DateTime for reminder
|
|
- `isReminderDone`: Completed flag
|
|
- `reminderRecurrence`: none, daily, weekly, monthly, custom
|
|
- `reminderLocation`: Location-based (future)
|
|
|
|
**Cron Job:**
|
|
- Route: `/api/cron/reminders`
|
|
- Triggered by external cron service
|
|
- Checks due reminders
|
|
- Sends notifications (nodemailer)
|
|
|
|
### Image Handling
|
|
|
|
**Storage Options:**
|
|
1. Base64 encoded in `Note.images` JSON array
|
|
2. File uploads to `public/uploads/notes/`
|
|
|
|
**Current:** Both supported
|
|
- Base64 for small images
|
|
- File uploads for larger images
|
|
|
|
**Future:** Move to CDN (S3, Cloudinary, etc.)
|
|
|
|
---
|
|
|
|
## Performance Architecture
|
|
|
|
### Server-Side Rendering (SSR)
|
|
- Faster initial page load
|
|
- SEO friendly
|
|
- Progressive enhancement
|
|
|
|
### Code Splitting
|
|
- Route-based splitting (automatic)
|
|
- Dynamic imports for heavy components
|
|
|
|
### Data Fetching
|
|
- React Cache for deduplication
|
|
- Server Actions for mutations
|
|
- Streaming responses
|
|
|
|
### Database Optimization
|
|
- Indexed fields (isPinned, isArchived, order, reminder, userId)
|
|
- Efficient queries with Prisma
|
|
- Connection pooling (limited in SQLite)
|
|
|
|
---
|
|
|
|
## Security Architecture
|
|
|
|
### Authentication
|
|
- NextAuth session management
|
|
- HTTP-only cookies
|
|
- CSRF protection (NextAuth built-in)
|
|
- Password hashing (bcrypt)
|
|
|
|
### Authorization
|
|
- Role-based access control (USER, ADMIN)
|
|
- Session validation in API routes
|
|
- Protected routes (middleware)
|
|
|
|
### Data Validation
|
|
- Zod schemas for input validation
|
|
- TypeScript for type safety
|
|
- SQL injection prevention (Prisma)
|
|
- XSS protection (React escaping)
|
|
|
|
### Future Security Enhancements
|
|
- Rate limiting
|
|
- CSRF tokens for forms
|
|
- Content Security Policy (CSP)
|
|
- HTTPS enforcement in production
|
|
|
|
---
|
|
|
|
## Deployment Architecture
|
|
|
|
### Current: Local Development
|
|
```bash
|
|
npm run dev # Next.js dev server
|
|
# Runs on http://localhost:3000
|
|
```
|
|
|
|
### Production Deployment (Planned)
|
|
**Container:** Docker
|
|
**Orchestration:** Docker Compose
|
|
**Process:**
|
|
1. Build Next.js app: `npm run build`
|
|
2. Start production server: `npm start`
|
|
3. Serve with Node.js or Docker
|
|
|
|
**Environment Variables:**
|
|
- `DATABASE_URL`: SQLite file path
|
|
- `NEXTAUTH_SECRET`: Session secret
|
|
- `NEXTAUTH_URL`: Application URL
|
|
- Email configuration (SMTP)
|
|
- AI provider API keys
|
|
|
|
---
|
|
|
|
## Monitoring & Observability
|
|
|
|
### Current: Basic
|
|
- Console logging
|
|
- Playwright test reports
|
|
- Prisma query logging (development)
|
|
|
|
### Future Needs
|
|
- Application monitoring (Sentry, LogRocket)
|
|
- Error tracking
|
|
- Performance monitoring
|
|
- Database query analysis
|
|
- User analytics
|
|
|
|
---
|
|
|
|
## Scalability Considerations
|
|
|
|
### Current Limitations (SQLite)
|
|
- Single writer (concurrent writes limited)
|
|
- File-based storage
|
|
- No automatic replication
|
|
- Manual backups needed
|
|
|
|
### Future Scaling Options
|
|
1. **PostgreSQL:** Replace SQLite with Postgres
|
|
2. **Connection Pooling:** PgBouncer
|
|
3. **Caching:** Redis for sessions and cache
|
|
4. **CDN:** CloudFlare, AWS CloudFront
|
|
5. **Object Storage:** S3 for images
|
|
6. **Load Balancing:** Multiple app instances
|
|
|
|
---
|
|
|
|
## Testing Architecture
|
|
|
|
### E2E Testing: Playwright
|
|
**Location:** `tests/search-quality.spec.ts`
|
|
**Coverage:** Search functionality
|
|
**Commands:**
|
|
- `npm test` - Run all tests
|
|
- `npm run test:ui` - UI mode
|
|
- `npm run test:headed` - Headed mode
|
|
|
|
### Test Reports
|
|
**Location:** `playwright-report/index.html`
|
|
**Results:** `test-results/.last-run.json`
|
|
|
|
---
|
|
|
|
## Web Vitals & Performance
|
|
|
|
### Core Web Vitals
|
|
- **LCP (Largest Contentful Paint):** Target < 2.5s
|
|
- **FID (First Input Delay):** Target < 100ms
|
|
- **CLS (Cumulative Layout Shift):** Target < 0.1
|
|
|
|
### Optimizations
|
|
- Next.js Image optimization
|
|
- Code splitting
|
|
- Server components (reduce JS bundle)
|
|
- Streaming responses
|
|
- Lazy loading images
|
|
|
|
---
|
|
|
|
## PWA Architecture
|
|
|
|
### Progressive Web App Features
|
|
**Package:** `@ducanh2912/next-pwa`
|
|
**Manifest:** `public/manifest.json`
|
|
|
|
**Features:**
|
|
- Offline support (future)
|
|
- Install as app (future)
|
|
- Push notifications (future)
|
|
- App shortcuts (future)
|
|
|
|
---
|
|
|
|
## Integration Points
|
|
|
|
### MCP Server
|
|
**Connection:** Database-mediated (shared SQLite)
|
|
**Location:** `../mcp-server/index.js`
|
|
**Protocol:** MCP (Model Context Protocol)
|
|
|
|
### Third-Party Services
|
|
- **Email:** nodemailer (SMTP)
|
|
- **AI:** OpenAI API, Ollama (local)
|
|
- **Future:** N8N workflows via MCP
|
|
|
|
---
|
|
|
|
## Development Workflow
|
|
|
|
### Local Development
|
|
```bash
|
|
cd keep-notes
|
|
npm install
|
|
npm run db:generate # Generate Prisma client
|
|
npm run dev # Start dev server
|
|
```
|
|
|
|
### Database Migrations
|
|
```bash
|
|
npx prisma migrate dev
|
|
npx prisma migrate deploy # Production
|
|
```
|
|
|
|
### Type Checking
|
|
```bash
|
|
npx tsc --noEmit # Type check only
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `next.config.ts` | Next.js configuration |
|
|
| `tsconfig.json` | TypeScript configuration |
|
|
| `tailwind.config.ts` | Tailwind CSS (if present) |
|
|
| `playwright.config.ts` | E2E test configuration |
|
|
| `auth.config.ts` | NextAuth configuration |
|
|
| `.env` | Environment variables |
|
|
|
|
---
|
|
|
|
## Architecture Decision Records
|
|
|
|
### Why Next.js App Router?
|
|
- Modern React features (Server Components)
|
|
- Built-in API routes
|
|
- File-based routing
|
|
- Excellent performance
|
|
- Strong community
|
|
|
|
### Why Prisma?
|
|
- Type-safe database access
|
|
- Excellent migration system
|
|
- Multiple database support
|
|
- Great developer experience
|
|
|
|
### Why SQLite?
|
|
- Zero configuration
|
|
- Portable (single file)
|
|
- Sufficient for single-user/small teams
|
|
- Easy local development
|
|
|
|
### Why No Redux/Zustand?
|
|
- Server Components reduce need for global state
|
|
- React Context sufficient for app state
|
|
- Server Actions simplify mutations
|
|
- Reduced bundle size
|
|
|
|
---
|
|
|
|
## Future Architecture Enhancements
|
|
|
|
### Short Term
|
|
1. Add Redis for caching
|
|
2. Implement rate limiting
|
|
3. Add error boundaries
|
|
4. Improve error logging
|
|
5. Add request tracing
|
|
|
|
### Long Term
|
|
1. Migrate to PostgreSQL
|
|
2. Add read replicas
|
|
3. Implement event sourcing
|
|
4. Add real-time features (WebSocket)
|
|
5. Microservices architecture
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The keep-notes application uses a modern JAMstack architecture with:
|
|
- **Next.js 16** for full-stack development
|
|
- **App Router** for routing and server components
|
|
- **Prisma** for type-safe database access
|
|
- **SQLite** for embedded database
|
|
- **NextAuth** for authentication
|
|
- **Radix UI** for accessible components
|
|
- **Vercel AI SDK** for AI features
|
|
- **Playwright** for E2E testing
|
|
|
|
This architecture provides a solid foundation for the Memento note-taking application with room for scaling and enhancement.
|