A comprehensive multi-tenant personal information management system API built with FastAPI and PostgreSQL.
- FastAPI-based REST API with comprehensive documentation
- Multi-tenant Architecture with role-based access control
- User Authentication & Authorization with JWT tokens
- Personal Daily Journaling with mood tracking, tags, and search
- PostgreSQL database integration with UUID primary keys
- Alembic database migrations for schema management
- Docker containerization for easy deployment
- CLI tools for database and user management
- Invitation System for team collaboration
- Subscription Management with member limits
- Comprehensive Validation with Pydantic schemas
The Recaller API includes a comprehensive daily journaling system with the following features:
- Daily Entries: Create one journal entry per day per tenancy
- Rich Content: Support for titles, detailed content, and metadata
- Word Count Tracking: Automatic word count calculation for entries
- Mood Tracking: Record daily mood with predefined options (happy, sad, anxious, etc.)
- Weather Logging: Track weather conditions for each entry
- Location Support: Record where the journal entry was written
- Tagging System: Add multiple tags to categorize entries
- Full-Text Search: Search through entry titles and content
- Advanced Filtering: Filter by date range, mood, tags, and favorites
- Favorites: Mark important entries as favorites
- Privacy Controls: Mark entries as private (default) or shareable
- Personal Prompts: Get daily writing prompts for inspiration
- Multiple Categories: Prompts organized by categories (gratitude, reflection, goals, etc.)
- Writing Streaks: Track current and longest writing streaks
- Statistics Dashboard: View total entries, monthly/yearly counts, and word counts
- Mood Analysis: See most common moods over time
- Progress Tracking: Monitor journaling habits and consistency
- Python 3.13+
- PostgreSQL (or use Docker)
- Docker and Docker Compose (optional)
- Clone the repository:
git clone <repository-url>
cd recaller-api- Create and activate a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Start the PostgreSQL database:
docker-compose up -d db- Initialize the database with migrations:
python cli.py db init- Create a PostgreSQL database named
recaller - Update the
DATABASE_URLenvironment variable or modifyalembic.ini - Initialize the database:
python cli.py db initThe project includes a CLI tool for managing database migrations:
- Initialize database:
python cli.py db init - Create new migration:
python cli.py db migrate -m "Migration message" - Apply migrations:
python cli.py db upgrade - Rollback migrations:
python cli.py db downgrade - Check migration status:
python cli.py db status - View migration history:
python cli.py db history
You can also use Alembic directly:
# Create a new migration
alembic revision --autogenerate -m "Add new table"
# Apply migrations
alembic upgrade head
# Rollback one migration
alembic downgrade -1
# Check current revision
alembic current
# View migration history
alembic historydocker-compose upThe API will be available at http://localhost:8000
- Ensure PostgreSQL is running and database is initialized
- Start the FastAPI server:
uvicorn api:app --reloadOnce the server is running, visit:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
recaller-api/
├── app/ # Main application package
│ ├── __init__.py
│ ├── core/ # Core functionality
│ │ ├── __init__.py
│ │ ├── auth.py # Authentication and authorization
│ │ └── database.py # Database configuration
│ ├── models/ # SQLAlchemy models
│ │ ├── __init__.py
│ │ ├── base.py # Base model class
│ │ ├── enums.py # Enum definitions
│ │ ├── user.py # User models
│ │ ├── tenancy.py # Tenancy models
│ │ ├── journal.py # Journal models
│ │ └── item.py # Item models
│ ├── routers/ # API route handlers
│ │ ├── __init__.py
│ │ └── journal.py # Journal endpoints
│ ├── schemas/ # Pydantic schemas
│ │ ├── __init__.py
│ │ └── journal.py # Journal schemas
│ ├── services/ # Business logic layer
│ │ ├── __init__.py
│ │ ├── base.py # Base service class
│ │ ├── user_service.py
│ │ ├── session_service.py
│ │ └── tenancy_service.py
│ └── utils/ # Utility functions
│ └── __init__.py
├── alembic/ # Database migrations
│ ├── versions/ # Migration scripts
│ └── env.py # Alembic environment configuration
├── alembic.ini # Alembic configuration
├── main.py # FastAPI application entry point
├── docker-compose.yml # Docker services
├── Dockerfile # API container
└── requirements.txt # Python dependencies
The API uses header-based tenancy instead of URL path parameters. Include the X-Tenancy-ID header in requests that require tenancy context:
curl -H "X-Tenancy-ID: your-tenancy-uuid" \
-H "Authorization: Bearer your-token" \
http://localhost:8000/journal/entriesThe application follows a layered architecture:
- Routers: Handle HTTP requests and responses
- Services: Contain business logic and data validation
- Models: Define database schema and relationships
- Schemas: Handle request/response serialization
POST /auth/register- Register new user (optionally with tenancy)POST /auth/login- User loginPOST /auth/logout- User logout
GET /users/me- Get current user infoPUT /users/me- Update current userGET /users/me/tenancies- Get user's tenanciesGET /users/me/invites- Get pending invites
POST /tenancies- Create new tenancyGET /tenancies/{id}- Get tenancy detailsPUT /tenancies/{id}- Update tenancy (admin/owner)PUT /tenancies/{id}/subscription- Update subscription (owner)
Requires X-Tenancy-ID header
POST /invites- Invite user (admin/owner)POST /invites/accept- Accept invitationPUT /members/{user_id}- Update member role (admin/owner)DELETE /members/{user_id}- Remove member (admin/owner)
Requires X-Tenancy-ID header
POST /items- Create itemGET /items- List itemsGET /items/{item_id}- Get itemPUT /items/{item_id}- Update itemDELETE /items/{item_id}- Delete item
POST /journal/entries- Create daily journal entryGET /journal/entries- List journal entries with filteringGET /journal/entries/{entry_id}- Get specific journal entryPUT /journal/entries/{entry_id}- Update journal entryDELETE /journal/entries/{entry_id}- Delete journal entryGET /journal/stats- Get journaling statistics and streaksGET /journal/prompts- Get random journal promptsGET /journal/prompts/daily- Get daily journal prompt
Note: All journal endpoints require the X-Tenancy-ID header to specify the tenancy context.
# Register a new user
curl -X POST "http://localhost:8000/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"username": "johndoe",
"password": "securepass123",
"first_name": "John",
"last_name": "Doe"
}'
# Login to get token
curl -X POST "http://localhost:8000/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securepass123"
}'# Create a journal entry (requires X-Tenancy-ID header)
curl -X POST "http://localhost:8000/journal/entries" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenancy-ID: YOUR_TENANCY_UUID" \
-d '{
"title": "My Daily Reflection",
"content": "Today was a productive day. I learned about FastAPI and made great progress on my project.",
"mood": "happy",
"weather": "sunny",
"tags": ["work", "learning", "productivity"]
}'
# Get journal entries with filtering
curl -X GET "http://localhost:8000/journal/entries?mood=happy&limit=10" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenancy-ID: YOUR_TENANCY_UUID"
# Get journaling statistics
curl -X GET "http://localhost:8000/journal/stats" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenancy-ID: YOUR_TENANCY_UUID"
# Get daily journal prompt
curl -X GET "http://localhost:8000/journal/prompts/daily" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenancy-ID: YOUR_TENANCY_UUID"The API includes a comprehensive CLI tool built with Typer for managing database operations and user management.
# Show available database commands
python cli.py db --help
# Create database tables
python cli.py db create
# Create a new migration
python cli.py db migrate --message "Description of changes"
# Apply migrations
python cli.py db upgrade
# Show current migration status
python cli.py db current
# Show migration history
python cli.py db history
# Downgrade migrations (use with caution)
python cli.py db downgrade
# Drop all tables (requires confirmation)
python cli.py db drop --confirm# Show available user commands
python cli.py user --help
# Create a new user
python cli.py user create \
--email user@example.com \
--username johndoe \
--first-name John \
--last-name Doe \
--password securepass123 \
--verified # Optional: mark as verified
# Verify a user's email
python cli.py user verify --email user@example.com
# Activate/deactivate user accounts
python cli.py user activate --email user@example.com
python cli.py user deactivate --email user@example.com
# List users
python cli.py user list --active-only --limit 20
# Get detailed user information
python cli.py user info --email user@example.com# Show available tenancy commands
python cli.py tenancy --help
# Create a new tenancy
python cli.py tenancy create \
--name "My Company" \
--slug my-company \
--owner-email owner@example.com \
--plan premium \
--description "Company tenancy"
# List tenancies
python cli.py tenancy list --limit 10
# Get detailed tenancy information
python cli.py tenancy info --slug my-company| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| String | User email (unique) | |
| username | String | Username (unique) |
| first_name | String | First name |
| last_name | String | Last name |
| hashed_password | String | Hashed password |
| is_active | Boolean | Account active status |
| is_verified | Boolean | Email verification status |
| created_at | DateTime | Creation timestamp |
| updated_at | DateTime | Last update timestamp |
| last_login | DateTime | Last login timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | String | Tenancy name |
| slug | String | URL-friendly identifier |
| description | Text | Tenancy description |
| owner_id | UUID | Foreign key to users |
| subscription_plan | String | Subscription plan |
| max_members | Integer | Maximum allowed members |
| is_active | Boolean | Tenancy active status |
| created_at | DateTime | Creation timestamp |
| updated_at | DateTime | Last update timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| user_id | UUID | Foreign key to users |
| tenancy_id | UUID | Foreign key to tenancies |
| role | String | User role (owner/admin/member) |
| is_active | Boolean | Membership active status |
| joined_at | DateTime | Join timestamp |
| updated_at | DateTime | Last update timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| tenancy_id | UUID | Foreign key to tenancies |
| invited_by_id | UUID | Foreign key to users |
| invited_user_id | UUID | Foreign key to users (nullable) |
| String | Invited email address | |
| role | String | Assigned role |
| invite_token | String | Unique invitation token |
| status | String | Invite status |
| expires_at | DateTime | Expiration timestamp |
| created_at | DateTime | Creation timestamp |
| accepted_at | DateTime | Acceptance timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| user_id | UUID | Foreign key to users |
| access_token | String | JWT access token |
| refresh_token | String | Refresh token |
| expires_at | DateTime | Token expiration |
| created_at | DateTime | Creation timestamp |
| is_active | Boolean | Session active status |
- Owner: Full control over tenancy, can manage subscription, add/remove admins
- Admin: Can manage members, invite users, manage tenancy settings
- Member: Can access tenancy resources, create/manage items
- Free: Up to 5 members
- Basic: Up to 15 members
- Premium: Up to 50 members
- Enterprise: Up to 200 members
- Admin/Owner invites user by email
- System generates unique invitation token
- Invited user receives invitation (token valid for 7 days)
- User accepts invitation to join tenancy
- User becomes active member with assigned role
curl -X POST "http://localhost:8000/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "owner@example.com",
"username": "owner",
"first_name": "John",
"last_name": "Doe",
"password": "securepass123",
"tenancy_name": "My Company"
}'curl -X POST "http://localhost:8000/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "owner@example.com",
"password": "securepass123"
}'curl -X POST "http://localhost:8000/tenancies/{tenancy_id}/invites" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "member@example.com",
"role": "member"
}'Start the FastAPI development server:
uvicorn main:app --reload --host 0.0.0.0 --port 8000The API will be available at:
- API Docs: http://localhost:8000/docs (Swagger UI)
- ReDoc: http://localhost:8000/redoc
- Health Check: http://localhost:8000/health
For production, use a production ASGI server like Gunicorn with Uvicorn workers:
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000Build and run with Docker Compose:
docker-compose up --buildThis will start:
- PostgreSQL database on port 5432
- API server on port 8000
DATABASE_URL: PostgreSQL connection string (default:postgresql://recaller:recaller@localhost:5432/recaller)SECRET_KEY: JWT secret key (change in production)
The application follows a modular architecture:
app/models/: SQLAlchemy database modelsapp/schemas/: Pydantic schemas for API validationapp/routers/: FastAPI route handlersapp/services/: Business logic layerapp/core/: Core functionality (auth, database)
When you modify models in app/models/, create a new migration:
alembic revision --autogenerate -m "Describe your changes"Then apply the migration:
alembic upgrade head- Define your model in the appropriate
app/models/file inheriting fromBase - Import the model in
app/models/__init__.py - Create a migration:
alembic revision --autogenerate -m "Add new model" - Apply the migration:
alembic upgrade head
- Create schemas in
app/schemas/ - Add business logic to
app/services/ - Create router in
app/routers/ - Include router in
main.py
- Ensure PostgreSQL is running
- Check the
DATABASE_URLin your environment oralembic.ini - Verify database credentials and permissions
- Check current migration status:
alembic current - View migration history:
alembic history - If needed, rollback and reapply:
alembic downgradethenalembic upgrade head
MIT License