## 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
16 KiB
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:
- Server State: Fetched from API, cached with React Cache
- URL State: Search params, route params
- Form State: Controlled components with useState
- Context: User session, theme preference
- 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:
{
"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 notesregister.ts: User registrationscrape.ts: Web scraping for link previews
Benefits:
- Type-safe (from schema)
- No API layer needed
- Direct database access
- Form validation (Zod)
Usage:
// 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:
// 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:
- User submits credentials
- NextAuth validates against database
- Session created in
Sessiontable - JWT token issued
- 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.tsis misnamed, should beopenai.tsor separate) - Ollama (
ollama.ts- local models)
Factory Pattern:
// 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:
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:
- User creates note →
NoteInputcomponent - Server action →
app/actions/notes.ts - Prisma create →
Notetable - Revalidate → UI updates
- Real-time → No WebSocket currently
Search:
- Text search: SQL
LIKEqueries (case-insensitive) - Semantic search: Vector embeddings (JSON field)
- Filtering: By labels, archived status, pinned status
Organization:
- Pinning:
isPinnedboolean - Archiving:
isArchivedboolean - Ordering:
orderfield (drag-drop) - Colors:
colorstring - Size:
size(small, medium, large)
Label System
Two Approaches:
- Label Table:
Labelmodel with user ownership - Note Labels: JSON array in
Note.labels
Current State: Both exist (migration artifact)
Labeltable: User-managed labelsNote.labels: JSON array of label names
Future: Consolidate to one approach
Reminder System
Fields:
reminder: DateTime for reminderisReminderDone: Completed flagreminderRecurrence: none, daily, weekly, monthly, customreminderLocation: Location-based (future)
Cron Job:
- Route:
/api/cron/reminders - Triggered by external cron service
- Checks due reminders
- Sends notifications (nodemailer)
Image Handling
Storage Options:
- Base64 encoded in
Note.imagesJSON array - 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
npm run dev # Next.js dev server
# Runs on http://localhost:3000
Production Deployment (Planned)
Container: Docker Orchestration: Docker Compose Process:
- Build Next.js app:
npm run build - Start production server:
npm start - Serve with Node.js or Docker
Environment Variables:
DATABASE_URL: SQLite file pathNEXTAUTH_SECRET: Session secretNEXTAUTH_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
- PostgreSQL: Replace SQLite with Postgres
- Connection Pooling: PgBouncer
- Caching: Redis for sessions and cache
- CDN: CloudFlare, AWS CloudFront
- Object Storage: S3 for images
- Load Balancing: Multiple app instances
Testing Architecture
E2E Testing: Playwright
Location: tests/search-quality.spec.ts
Coverage: Search functionality
Commands:
npm test- Run all testsnpm run test:ui- UI modenpm 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
cd keep-notes
npm install
npm run db:generate # Generate Prisma client
npm run dev # Start dev server
Database Migrations
npx prisma migrate dev
npx prisma migrate deploy # Production
Type Checking
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
- Add Redis for caching
- Implement rate limiting
- Add error boundaries
- Improve error logging
- Add request tracing
Long Term
- Migrate to PostgreSQL
- Add read replicas
- Implement event sourcing
- Add real-time features (WebSocket)
- 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.