686 lines
17 KiB
YAML
686 lines
17 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: HelpYourNeighbour API
|
|
version: 0.2.0
|
|
description: API contract for authentication, requests, offers, contacts, profile, addresses, and reviews.
|
|
servers:
|
|
- url: http://localhost:3000
|
|
description: Local development
|
|
security:
|
|
- bearerAuth: []
|
|
paths:
|
|
/health:
|
|
get:
|
|
tags: [system]
|
|
summary: Health check
|
|
security: []
|
|
responses:
|
|
'200':
|
|
description: Service healthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/HealthResponse'
|
|
|
|
/auth/register:
|
|
post:
|
|
tags: [auth]
|
|
summary: Register a new user
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterRequest'
|
|
responses:
|
|
'201':
|
|
description: User created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AuthTokenResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'409':
|
|
description: Email already exists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'500':
|
|
description: Registration failed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/auth/login:
|
|
post:
|
|
tags: [auth]
|
|
summary: Login with email/password
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginRequest'
|
|
responses:
|
|
'200':
|
|
description: Login success
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AuthTokenResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
description: Invalid credentials
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/requests:
|
|
get:
|
|
tags: [requests]
|
|
summary: List help requests
|
|
security: []
|
|
responses:
|
|
'200':
|
|
description: Request list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/HelpRequest'
|
|
post:
|
|
tags: [requests]
|
|
summary: Create help request
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateHelpRequest'
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IdResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/offers/{requestId}:
|
|
post:
|
|
tags: [offers]
|
|
summary: Create offer for help request
|
|
parameters:
|
|
- $ref: '#/components/parameters/RequestId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateOfferRequest'
|
|
responses:
|
|
'201':
|
|
description: Offer created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IdResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequestSimple'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/offers/negotiation/{offerId}:
|
|
post:
|
|
tags: [offers]
|
|
summary: Create counter-offer/negotiation message
|
|
parameters:
|
|
- $ref: '#/components/parameters/OfferId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateOfferRequest'
|
|
responses:
|
|
'201':
|
|
description: Negotiation entry created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IdResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequestSimple'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/offers/accept/{offerId}:
|
|
post:
|
|
tags: [offers]
|
|
summary: Accept offer and create deal
|
|
parameters:
|
|
- $ref: '#/components/parameters/OfferId'
|
|
responses:
|
|
'201':
|
|
description: Deal created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DealCreatedResponse'
|
|
'400':
|
|
description: Invalid offer id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
description: Offer not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/contacts/request:
|
|
post:
|
|
tags: [contacts]
|
|
summary: Request contact exchange for a deal
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ContactRequestCreate'
|
|
responses:
|
|
'201':
|
|
description: Request created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IdResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
description: Deal not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'409':
|
|
description: Request already exists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/contacts/respond:
|
|
post:
|
|
tags: [contacts]
|
|
summary: Accept or reject contact exchange request
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ContactRequestRespond'
|
|
responses:
|
|
'200':
|
|
description: Response processed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/StatusResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
description: Request not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/contacts/deal/{dealId}:
|
|
get:
|
|
tags: [contacts]
|
|
summary: List accepted contact exchanges for deal
|
|
parameters:
|
|
- $ref: '#/components/parameters/DealId'
|
|
responses:
|
|
'200':
|
|
description: Accepted contact exchange rows
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ContactExchangeRow'
|
|
'400':
|
|
description: Invalid dealId
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
description: Deal not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/addresses/change-request:
|
|
post:
|
|
tags: [addresses]
|
|
summary: Request address change with postal verification code
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AddressChangeRequest'
|
|
responses:
|
|
'201':
|
|
description: Change request created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AddressChangeCreatedResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/addresses/verify:
|
|
post:
|
|
tags: [addresses]
|
|
summary: Verify address change with 6-digit code
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AddressVerifyRequest'
|
|
responses:
|
|
'200':
|
|
description: Address verified
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/StatusResponse'
|
|
'400':
|
|
description: Invalid payload or code
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
description: Request not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'409':
|
|
description: Request not pending
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/profile/phone:
|
|
post:
|
|
tags: [profile]
|
|
summary: Update encrypted phone number
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PhoneUpdateRequest'
|
|
responses:
|
|
'200':
|
|
description: Phone updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/StatusResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/reviews/{dealId}:
|
|
post:
|
|
tags: [reviews]
|
|
summary: Create review for deal
|
|
parameters:
|
|
- $ref: '#/components/parameters/DealId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateReviewRequest'
|
|
responses:
|
|
'201':
|
|
description: Review created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/IdResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequestSimple'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
|
|
parameters:
|
|
RequestId:
|
|
name: requestId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
OfferId:
|
|
name: offerId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
DealId:
|
|
name: dealId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
|
|
responses:
|
|
BadRequest:
|
|
description: Validation failed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
BadRequestSimple:
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
Unauthorized:
|
|
description: Missing or invalid token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
Forbidden:
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
schemas:
|
|
HealthResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: ok
|
|
required: [status]
|
|
|
|
ErrorResponse:
|
|
type: object
|
|
properties:
|
|
error:
|
|
oneOf:
|
|
- type: string
|
|
- type: object
|
|
|
|
StatusResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
status:
|
|
type: string
|
|
required: [status]
|
|
|
|
IdResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
id:
|
|
type: integer
|
|
minimum: 1
|
|
required: [id]
|
|
|
|
DealCreatedResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
dealId:
|
|
type: integer
|
|
minimum: 1
|
|
required: [dealId]
|
|
|
|
AuthTokenResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
token:
|
|
type: string
|
|
required: [token]
|
|
|
|
RegisterRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
minLength: 8
|
|
displayName:
|
|
type: string
|
|
minLength: 2
|
|
maxLength: 120
|
|
required: [email, password, displayName]
|
|
|
|
LoginRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
minLength: 1
|
|
required: [email, password]
|
|
|
|
HelpRequest:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
title:
|
|
type: string
|
|
description:
|
|
type: string
|
|
value_chf:
|
|
type: number
|
|
status:
|
|
type: string
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
requester_name:
|
|
type: string
|
|
|
|
CreateHelpRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
title:
|
|
type: string
|
|
minLength: 3
|
|
maxLength: 180
|
|
description:
|
|
type: string
|
|
minLength: 5
|
|
valueChf:
|
|
type: number
|
|
exclusiveMinimum: 0
|
|
required: [title, description, valueChf]
|
|
|
|
CreateOfferRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
amountChf:
|
|
type: number
|
|
exclusiveMinimum: 0
|
|
message:
|
|
type: string
|
|
maxLength: 2000
|
|
required: [amountChf]
|
|
|
|
ContactRequestCreate:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
dealId:
|
|
type: integer
|
|
minimum: 1
|
|
targetUserId:
|
|
type: integer
|
|
minimum: 1
|
|
required: [dealId, targetUserId]
|
|
|
|
ContactRequestRespond:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
requestId:
|
|
type: integer
|
|
minimum: 1
|
|
accept:
|
|
type: boolean
|
|
required: [requestId, accept]
|
|
|
|
ContactExchangeRow:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
requester_id:
|
|
type: integer
|
|
target_id:
|
|
type: integer
|
|
accepted:
|
|
type: boolean
|
|
requester_phone_encrypted:
|
|
type: string
|
|
target_phone_encrypted:
|
|
type: string
|
|
|
|
AddressChangeRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
newAddress:
|
|
type: string
|
|
minLength: 10
|
|
required: [newAddress]
|
|
|
|
AddressChangeCreatedResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
requestId:
|
|
type: integer
|
|
minimum: 1
|
|
postalDispatch:
|
|
type: string
|
|
example: pending_letter
|
|
note:
|
|
type: string
|
|
verificationCode:
|
|
type: string
|
|
pattern: '^\\d{6}$'
|
|
required: [requestId, postalDispatch, note, verificationCode]
|
|
|
|
AddressVerifyRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
requestId:
|
|
type: integer
|
|
minimum: 1
|
|
code:
|
|
type: string
|
|
pattern: '^\\d{6}$'
|
|
required: [requestId, code]
|
|
|
|
PhoneUpdateRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
phone:
|
|
type: string
|
|
minLength: 6
|
|
maxLength: 40
|
|
required: [phone]
|
|
|
|
CreateReviewRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
properties:
|
|
revieweeId:
|
|
type: integer
|
|
minimum: 1
|
|
rating:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 5
|
|
comment:
|
|
type: string
|
|
maxLength: 2000
|
|
required: [revieweeId, rating]
|