first commit
Made-with: Cursor
This commit is contained in:
279
README.md
Normal file
279
README.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# Calendar & Contacts API
|
||||
|
||||
A production-grade REST API for calendar management, event scheduling, contacts, availability queries, and public booking links. Built with Go, PostgreSQL, and designed for human users, AI agents, and programmatic automation.
|
||||
|
||||
## Features
|
||||
|
||||
- **Calendars** - Create, share, and manage multiple calendars with role-based access (owner/editor/viewer)
|
||||
- **Events** - Full CRUD with recurring events (RFC 5545 RRULE), reminders, attendees, tags, and attachments
|
||||
- **Contacts** - Personal contact management with search
|
||||
- **Availability** - Query busy/free time across calendars
|
||||
- **Booking Links** - Public scheduling pages with configurable working hours, duration, and buffer time
|
||||
- **ICS Import/Export** - Standard iCalendar format compatibility
|
||||
- **Dual Auth** - JWT tokens for interactive use, scoped API keys for agents and automation
|
||||
- **Background Jobs** - Reminder notifications via Redis + Asynq (optional)
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|-----------|
|
||||
| Language | Go 1.24 |
|
||||
| Router | Chi |
|
||||
| Database | PostgreSQL 15+ |
|
||||
| Query Layer | sqlc |
|
||||
| Migrations | golang-migrate |
|
||||
| Auth | JWT (golang-jwt/jwt/v5) + bcrypt |
|
||||
| Recurrence | rrule-go (RFC 5545) |
|
||||
| Background Jobs | Asynq (Redis) |
|
||||
| API Docs | OpenAPI 3.1.0 + Swagger UI |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.24+
|
||||
- PostgreSQL 15+
|
||||
- Redis (optional, for background reminder jobs)
|
||||
|
||||
### Setup
|
||||
|
||||
1. Clone the repository and navigate to the project directory.
|
||||
|
||||
2. Copy the environment file and configure it:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
3. Edit `.env` with your database credentials:
|
||||
|
||||
```env
|
||||
DATABASE_URL=postgres://calendarapi:password@localhost:5432/calendarapi?sslmode=disable
|
||||
JWT_SECRET=your-secret-key-change-me
|
||||
SERVER_PORT=3019
|
||||
ENV=development
|
||||
# REDIS_ADDR=localhost:6379 # uncomment to enable background jobs
|
||||
```
|
||||
|
||||
4. Create the database:
|
||||
|
||||
```bash
|
||||
createdb calendarapi
|
||||
```
|
||||
|
||||
5. Run the server (migrations run automatically on startup):
|
||||
|
||||
```bash
|
||||
go run cmd/server/main.go
|
||||
```
|
||||
|
||||
The server starts on `http://localhost:3019`. Swagger UI is available at `http://localhost:3019/docs`.
|
||||
|
||||
## API Documentation
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| Swagger UI | http://localhost:3019/docs |
|
||||
| OpenAPI Spec | http://localhost:3019/openapi.json |
|
||||
| LLM Reference | [llms.txt](llms.txt) |
|
||||
| Agent Skill | [SKILL.md](SKILL.md) |
|
||||
|
||||
## Authentication
|
||||
|
||||
### JWT (for users)
|
||||
|
||||
Register or login to receive an access token (15 min) and refresh token:
|
||||
|
||||
```bash
|
||||
# Register
|
||||
curl -X POST http://localhost:3019/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email": "user@example.com", "password": "securepassword123", "timezone": "UTC"}'
|
||||
|
||||
# Login
|
||||
curl -X POST http://localhost:3019/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email": "user@example.com", "password": "securepassword123"}'
|
||||
```
|
||||
|
||||
Use the access token in subsequent requests:
|
||||
|
||||
```
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
### API Keys (for agents)
|
||||
|
||||
Create a scoped API key for long-lived programmatic access:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3019/api-keys \
|
||||
-H "Authorization: Bearer <access_token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "my-agent",
|
||||
"scopes": {
|
||||
"calendars": ["read", "write"],
|
||||
"events": ["read", "write"],
|
||||
"contacts": ["read", "write"],
|
||||
"availability": ["read"],
|
||||
"booking": ["write"]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Use the returned token in subsequent requests:
|
||||
|
||||
```
|
||||
X-API-Key: <token>
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Auth
|
||||
|
||||
| Method | Endpoint | Auth | Description |
|
||||
|--------|----------|------|-------------|
|
||||
| POST | `/auth/register` | None | Create account |
|
||||
| POST | `/auth/login` | None | Login |
|
||||
| POST | `/auth/refresh` | None | Refresh JWT tokens |
|
||||
| POST | `/auth/logout` | Yes | Revoke refresh token |
|
||||
| GET | `/auth/me` | Yes | Get current user |
|
||||
|
||||
### Users
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/users/me` | Get profile |
|
||||
| PUT | `/users/me` | Update profile |
|
||||
| DELETE | `/users/me` | Delete account |
|
||||
|
||||
### API Keys
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/api-keys` | Create API key |
|
||||
| GET | `/api-keys` | List API keys |
|
||||
| DELETE | `/api-keys/{id}` | Revoke API key |
|
||||
|
||||
### Calendars
|
||||
|
||||
| Method | Endpoint | Scope | Description |
|
||||
|--------|----------|-------|-------------|
|
||||
| GET | `/calendars` | calendars:read | List calendars |
|
||||
| POST | `/calendars` | calendars:write | Create calendar |
|
||||
| GET | `/calendars/{id}` | calendars:read | Get calendar |
|
||||
| PUT | `/calendars/{id}` | calendars:write | Update calendar |
|
||||
| DELETE | `/calendars/{id}` | calendars:write | Delete calendar |
|
||||
| POST | `/calendars/{id}/share` | calendars:write | Share calendar |
|
||||
| GET | `/calendars/{id}/members` | calendars:read | List members |
|
||||
| DELETE | `/calendars/{id}/members/{userID}` | calendars:write | Remove member |
|
||||
|
||||
### Events
|
||||
|
||||
| Method | Endpoint | Scope | Description |
|
||||
|--------|----------|-------|-------------|
|
||||
| GET | `/events` | events:read | List events in time range |
|
||||
| POST | `/events` | events:write | Create event |
|
||||
| GET | `/events/{id}` | events:read | Get event |
|
||||
| PUT | `/events/{id}` | events:write | Update event |
|
||||
| DELETE | `/events/{id}` | events:write | Delete event |
|
||||
| POST | `/events/{id}/reminders` | events:write | Add reminders |
|
||||
| DELETE | `/events/{id}/reminders/{reminderID}` | events:write | Remove reminder |
|
||||
| POST | `/events/{id}/attendees` | events:write | Add attendees |
|
||||
| PUT | `/events/{id}/attendees/{attendeeID}` | events:write | Update RSVP |
|
||||
| DELETE | `/events/{id}/attendees/{attendeeID}` | events:write | Remove attendee |
|
||||
|
||||
### Contacts
|
||||
|
||||
| Method | Endpoint | Scope | Description |
|
||||
|--------|----------|-------|-------------|
|
||||
| GET | `/contacts` | contacts:read | List contacts |
|
||||
| POST | `/contacts` | contacts:write | Create contact |
|
||||
| GET | `/contacts/{id}` | contacts:read | Get contact |
|
||||
| PUT | `/contacts/{id}` | contacts:write | Update contact |
|
||||
| DELETE | `/contacts/{id}` | contacts:write | Delete contact |
|
||||
|
||||
### Availability
|
||||
|
||||
| Method | Endpoint | Scope | Description |
|
||||
|--------|----------|-------|-------------|
|
||||
| GET | `/availability` | availability:read | Get busy blocks for a calendar |
|
||||
|
||||
### Booking
|
||||
|
||||
| Method | Endpoint | Auth | Description |
|
||||
|--------|----------|------|-------------|
|
||||
| POST | `/calendars/{id}/booking-link` | booking:write | Create booking link |
|
||||
| GET | `/booking/{token}/availability` | None | Get available slots |
|
||||
| POST | `/booking/{token}/reserve` | None | Reserve a slot |
|
||||
|
||||
### ICS
|
||||
|
||||
| Method | Endpoint | Scope | Description |
|
||||
|--------|----------|-------|-------------|
|
||||
| GET | `/calendars/{id}/export.ics` | calendars:read | Export as ICS |
|
||||
| POST | `/calendars/import` | calendars:write | Import ICS file |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
calendarapi/
|
||||
cmd/server/main.go Entry point
|
||||
internal/
|
||||
api/
|
||||
routes.go Route definitions
|
||||
handlers/ HTTP handlers
|
||||
openapi/ OpenAPI spec files + Swagger UI
|
||||
auth/ JWT manager
|
||||
config/ Environment config
|
||||
middleware/ Auth, rate limiting, scope enforcement
|
||||
models/ Domain models
|
||||
repository/ sqlc-generated database queries
|
||||
scheduler/ Asynq background job scheduler
|
||||
service/ Business logic layer
|
||||
utils/ Shared utilities
|
||||
migrations/ SQL migration files
|
||||
sqlc/ sqlc configuration
|
||||
about/ Design specifications
|
||||
overview.md Architecture and data model
|
||||
logic.md Business logic rules
|
||||
details.md Endpoint contracts
|
||||
SKILL.md AI agent integration guide
|
||||
llms.txt LLM-optimized API reference
|
||||
.env.example Environment template
|
||||
go.mod
|
||||
go.sum
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Client (Web / Mobile / Agent)
|
||||
|
|
||||
HTTP REST API (Chi router)
|
||||
|
|
||||
Middleware (Auth, Rate Limiting, Scope Enforcement)
|
||||
|
|
||||
Handler Layer (Parse request, validate, return JSON)
|
||||
|
|
||||
Service Layer (Business logic, permissions, transactions)
|
||||
|
|
||||
Repository Layer (sqlc queries)
|
||||
|
|
||||
PostgreSQL
|
||||
```
|
||||
|
||||
## Design Principles
|
||||
|
||||
- **Stateless** - JWT or API key on every request, no server-side sessions
|
||||
- **UTC everywhere** - All timestamps stored in UTC, original timezone preserved
|
||||
- **Soft deletes** - No data is permanently destroyed
|
||||
- **Strict ownership** - Ownership derived from auth context, never from client input
|
||||
- **Cursor pagination** - Efficient, consistent pagination on all list endpoints
|
||||
- **Scoped API keys** - Fine-grained permission control for agent access
|
||||
- **Race-safe booking** - Database transactions with row locking prevent double-booking
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Reference in New Issue
Block a user