Files
Michilis bd24545b7b Fix BASE_URL config loading, add tasks/projects; robust .env path resolution
- Config: try ENV_FILE, .env, ../.env for loading; trim trailing slash from BaseURL
- Log BASE_URL at server startup for verification
- .env.example: document BASE_URL
- Tasks, projects, tags, migrations and related API/handlers

Made-with: Cursor
2026-03-09 18:57:51 +00:00

275 lines
13 KiB
JSON

{
"components": {
"securitySchemes": {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "JWT access token obtained from /auth/login or /auth/register"
},
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key",
"description": "API key token for agent/programmatic access with scoped permissions"
}
},
"schemas": {
"Error": {
"type": "object",
"required": ["error", "code"],
"properties": {
"error": { "type": "string", "description": "Human-readable error message" },
"code": { "type": "string", "description": "Machine-readable error code", "enum": ["VALIDATION_ERROR", "AUTH_REQUIRED", "AUTH_INVALID", "FORBIDDEN", "NOT_FOUND", "CONFLICT", "RATE_LIMITED", "INTERNAL"] },
"details": { "description": "Additional error context" }
}
},
"OkResponse": {
"type": "object",
"required": ["ok"],
"properties": {
"ok": { "type": "boolean", "example": true }
}
},
"PageInfo": {
"type": "object",
"required": ["limit", "next_cursor"],
"properties": {
"limit": { "type": "integer", "example": 50 },
"next_cursor": { "type": "string", "nullable": true, "description": "Opaque cursor for next page, null if no more results" }
}
},
"User": {
"type": "object",
"required": ["id", "email", "timezone", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"email": { "type": "string", "format": "email" },
"timezone": { "type": "string", "example": "America/New_York", "description": "IANA timezone name" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
}
},
"Calendar": {
"type": "object",
"required": ["id", "name", "color", "is_public", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"name": { "type": "string", "minLength": 1, "maxLength": 80 },
"color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$", "example": "#22C55E" },
"is_public": { "type": "boolean" },
"ical_url": { "type": "string", "format": "uri", "description": "iCal feed URL. Present for both public and private calendars. Public calendars use a shorter token; private calendars use a 64-character SHA256 hex token for additional security." },
"role": { "type": "string", "enum": ["owner", "editor", "viewer"], "description": "Current user's role on this calendar" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
}
},
"Reminder": {
"type": "object",
"required": ["id", "minutes_before"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"minutes_before": { "type": "integer", "minimum": 0, "maximum": 10080 }
}
},
"Attendee": {
"type": "object",
"required": ["id", "status"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"user_id": { "type": "string", "format": "uuid", "nullable": true },
"email": { "type": "string", "format": "email", "nullable": true },
"status": { "type": "string", "enum": ["pending", "accepted", "declined", "tentative"] }
}
},
"Attachment": {
"type": "object",
"required": ["id", "file_url"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"file_url": { "type": "string", "format": "uri" }
}
},
"Event": {
"type": "object",
"required": ["id", "calendar_id", "title", "start_time", "end_time", "timezone", "all_day", "created_by", "updated_by", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"calendar_id": { "type": "string", "format": "uuid" },
"title": { "type": "string", "minLength": 1, "maxLength": 140 },
"description": { "type": "string", "nullable": true },
"location": { "type": "string", "nullable": true },
"start_time": { "type": "string", "format": "date-time", "description": "UTC start time in RFC3339" },
"end_time": { "type": "string", "format": "date-time", "description": "UTC end time in RFC3339" },
"timezone": { "type": "string", "example": "America/Asuncion", "description": "Original IANA timezone" },
"all_day": { "type": "boolean" },
"recurrence_rule": { "type": "string", "nullable": true, "description": "RFC5545 RRULE string", "example": "FREQ=WEEKLY;BYDAY=MO,WE,FR" },
"is_occurrence": { "type": "boolean", "description": "True if this is an expanded recurrence occurrence" },
"occurrence_start_time": { "type": "string", "format": "date-time", "nullable": true },
"occurrence_end_time": { "type": "string", "format": "date-time", "nullable": true },
"created_by": { "type": "string", "format": "uuid" },
"updated_by": { "type": "string", "format": "uuid" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"reminders": { "type": "array", "items": { "$ref": "#/components/schemas/Reminder" } },
"attendees": { "type": "array", "items": { "$ref": "#/components/schemas/Attendee" } },
"tags": { "type": "array", "items": { "type": "string" } },
"attachments": { "type": "array", "items": { "$ref": "#/components/schemas/Attachment" } }
}
},
"Contact": {
"type": "object",
"required": ["id", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"first_name": { "type": "string", "nullable": true },
"last_name": { "type": "string", "nullable": true },
"email": { "type": "string", "format": "email", "nullable": true },
"phone": { "type": "string", "nullable": true },
"company": { "type": "string", "nullable": true },
"notes": { "type": "string", "nullable": true },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
}
},
"APIKeyResponse": {
"type": "object",
"required": ["id", "name", "created_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"name": { "type": "string" },
"created_at": { "type": "string", "format": "date-time" },
"revoked_at": { "type": "string", "format": "date-time", "nullable": true },
"token": { "type": "string", "description": "Raw token, only returned once on creation" }
}
},
"CalendarMember": {
"type": "object",
"required": ["user_id", "email", "role"],
"properties": {
"user_id": { "type": "string", "format": "uuid" },
"email": { "type": "string", "format": "email" },
"role": { "type": "string", "enum": ["owner", "editor", "viewer"] }
}
},
"CalendarSubscription": {
"type": "object",
"required": ["id", "calendar_id", "source_url", "created_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"calendar_id": { "type": "string", "format": "uuid" },
"source_url": { "type": "string", "format": "uri", "description": "iCal feed URL" },
"last_synced_at": { "type": "string", "format": "date-time", "nullable": true },
"sync_interval_minutes": { "type": "integer", "nullable": true },
"created_at": { "type": "string", "format": "date-time" }
}
},
"BusyBlock": {
"type": "object",
"required": ["start", "end", "event_id"],
"properties": {
"start": { "type": "string", "format": "date-time" },
"end": { "type": "string", "format": "date-time" },
"event_id": { "type": "string", "format": "uuid" }
}
},
"WorkingHourSlot": {
"type": "object",
"required": ["start", "end"],
"properties": {
"start": { "type": "string", "example": "09:00", "pattern": "^\\d{2}:\\d{2}$" },
"end": { "type": "string", "example": "17:00", "pattern": "^\\d{2}:\\d{2}$" }
}
},
"TimeSlot": {
"type": "object",
"required": ["start", "end"],
"properties": {
"start": { "type": "string", "format": "date-time" },
"end": { "type": "string", "format": "date-time" }
}
},
"Task": {
"type": "object",
"required": ["id", "title", "status", "priority", "owner_id", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"title": { "type": "string", "minLength": 1, "maxLength": 500 },
"description": { "type": "string", "nullable": true, "description": "Markdown supported" },
"status": { "type": "string", "enum": ["todo", "in_progress", "done", "archived"] },
"priority": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
"due_date": { "type": "string", "format": "date-time", "nullable": true },
"completed_at": { "type": "string", "format": "date-time", "nullable": true },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"owner_id": { "type": "string", "format": "uuid" },
"project_id": { "type": "string", "format": "uuid", "nullable": true },
"parent_id": { "type": "string", "format": "uuid", "nullable": true },
"subtasks": { "type": "array", "items": { "$ref": "#/components/schemas/Task" } },
"tags": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } },
"completion_percentage": { "type": "integer", "nullable": true }
}
},
"Project": {
"type": "object",
"required": ["id", "owner_id", "name", "color", "is_shared", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"owner_id": { "type": "string", "format": "uuid" },
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$", "example": "#3B82F6" },
"is_shared": { "type": "boolean" },
"deadline": { "type": "string", "format": "date-time", "nullable": true },
"sort_order": { "type": "integer" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
}
},
"Tag": {
"type": "object",
"required": ["id", "owner_id", "name", "color", "created_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"owner_id": { "type": "string", "format": "uuid" },
"name": { "type": "string", "minLength": 1, "maxLength": 50 },
"color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$", "example": "#6B7280" },
"created_at": { "type": "string", "format": "date-time" }
}
},
"ProjectMember": {
"type": "object",
"required": ["user_id", "email", "role"],
"properties": {
"user_id": { "type": "string", "format": "uuid" },
"email": { "type": "string", "format": "email" },
"role": { "type": "string", "enum": ["owner", "editor", "viewer"] }
}
},
"TaskReminder": {
"type": "object",
"required": ["id", "task_id", "type", "scheduled_at", "created_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"task_id": { "type": "string", "format": "uuid" },
"type": { "type": "string", "enum": ["push", "email", "webhook", "telegram", "nostr"] },
"config": { "type": "object", "additionalProperties": true },
"scheduled_at": { "type": "string", "format": "date-time" },
"created_at": { "type": "string", "format": "date-time" }
}
},
"TaskWebhook": {
"type": "object",
"required": ["id", "owner_id", "url", "events", "created_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"owner_id": { "type": "string", "format": "uuid" },
"url": { "type": "string", "format": "uri" },
"events": { "type": "array", "items": { "type": "string", "enum": ["created", "status_change", "completion"] } },
"secret": { "type": "string", "nullable": true },
"created_at": { "type": "string", "format": "date-time" }
}
}
}
}
}