# Carry Your Live - Backend API

REST API for the Carry Your Live task management app with offline-first sync support.

## Tech Stack

- **Runtime**: Node.js 20+ with TypeScript
- **Framework**: Express.js
- **Database**: PostgreSQL with Drizzle ORM
- **Authentication**: JWT (JSON Web Tokens)
- **Validation**: Zod

## Quick Start

### Option A — Docker (Postgres + API in containers)

Requires Docker. Runs the database **and** the Node API together:

```bash
docker compose up --build
```

This starts:
- `carry-your-live-db` — PostgreSQL 16 on port 5432
- `carry-your-live-api` — the API in dev mode (tsx watch, hot reload) on port 3000, schema auto-applied on start

Server runs at `http://localhost:3000` (health check: `GET /health`).

For production-style serving of the built app:

```bash
# Build and run the slim prod image instead
docker build --target prod -t carry-your-live-api:prod .
docker run --rm -p 3000:3000 --env-file .env carry-your-live-api:prod
```

### Option B — Local Node + Docker Postgres

### Prerequisites

- Node.js 20+
- PostgreSQL 16+ (or use Docker)

### Setup

1. **Start PostgreSQL** (using Docker):
   ```bash
   docker-compose up -d postgres
   ```

2. **Install dependencies**:
   ```bash
   npm install
   ```

3. **Configure environment**:
   ```bash
   cp .env.example .env
   # Edit .env with your settings
   ```

4. **Run database migrations**:
   ```bash
   npm run db:push
   ```

5. **Start development server**:
   ```bash
   npm run dev
   ```

Server runs at `http://localhost:3000`

## API Endpoints

### Authentication
- `POST /api/auth/register` - Register new user
- `POST /api/auth/login` - Login
- `GET /api/auth/me` - Get current user

### Categories
- `GET /api/categories` - List categories
- `POST /api/categories` - Create category
- `PATCH /api/categories/:id` - Update category
- `DELETE /api/categories/:id` - Delete category

### Tasks
- `GET /api/tasks` - List tasks (with filters)
- `GET /api/tasks/:id` - Get task with subtasks
- `POST /api/tasks` - Create task
- `PATCH /api/tasks/:id` - Update task
- `DELETE /api/tasks/:id` - Delete task
- `POST /api/tasks/batch` - Batch operations

### Subtasks
- `GET /api/subtasks/task/:taskId` - List subtasks for task
- `POST /api/subtasks/task/:taskId` - Create subtask
- `PATCH /api/subtasks/:id` - Update subtask
- `DELETE /api/subtasks/:id` - Delete subtask

### Users
- `GET /api/users/me` - Get user with settings
- `PATCH /api/users/me/settings` - Update settings

### Sync (Offline-first)
- `GET /api/sync?since=<timestamp>` - Pull changes since timestamp
- `POST /api/sync/push` - Push local changes

## Database Schema

See `src/db/schema.ts` for Drizzle schema definitions.

## Sync Protocol

The sync endpoint uses a cursor-based approach:

1. **Pull**: Client sends `since` timestamp, server returns all changes since then
2. **Push**: Client sends batched changes with `lastPulledAt`, server applies with conflict resolution (last-write-wins)

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `DATABASE_URL` | PostgreSQL connection string | Required |
| `JWT_SECRET` | Secret for JWT signing | Required (32+ chars) |
| `PORT` | Server port | 3000 |
| `NODE_ENV` | Environment | development |
| `FRONTEND_URL` | CORS origin | http://localhost:8081 |

## Production Deployment

1. Set `NODE_ENV=production`
2. Use strong `JWT_SECRET` (32+ random chars)
3. Configure proper `DATABASE_URL`
4. Run `npm run build` then `npm start`
5. Use process manager (PM2, systemd) or container orchestration

The included `Dockerfile` has a `prod` stage that builds TypeScript and serves `dist/`
with production-only dependencies on a minimal `node:20-alpine` image.

## License

MIT