openapi: 3.0.3 info: title: REST API version: 1.0.0 description: API użytkowników, klientów i samochodów. Wszystkie endpointy wymagają HTTP Basic Auth; w Swagger UI wybierz Authorize i podaj login oraz hasło. servers: - url: / security: - basicAuth: [] tags: - name: Auth - name: Users - name: Customers - name: Cars paths: /api/auth/login: post: tags: [Auth] summary: Potwierdź logowanie i zaktualizuj ostatnie logowanie responses: '200': description: Uwierzytelniony użytkownik content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '401': { $ref: '#/components/responses/Unauthorized' } /api/auth/me: get: tags: [Auth] summary: Pobierz własny profil responses: '200': description: Profil użytkownika content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } put: tags: [Auth] summary: Zaktualizuj własny profil requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ProfileInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '422': { $ref: '#/components/responses/ValidationError' } patch: tags: [Auth] summary: Częściowo zaktualizuj własny profil requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ProfileInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '422': { $ref: '#/components/responses/ValidationError' } /api/users: get: tags: [Users] summary: Lista użytkowników (admin) parameters: - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' - name: q in: query schema: { type: string } - name: role in: query schema: { type: string, enum: [admin, user] } - name: active in: query schema: { type: boolean } responses: '200': description: Lista użytkowników content: application/json: schema: { $ref: '#/components/schemas/UserList' } '403': { $ref: '#/components/responses/Forbidden' } post: tags: [Users] summary: Dodaj użytkownika (admin) requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UserCreate' } responses: '201': description: Utworzono content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '422': { $ref: '#/components/responses/ValidationError' } /api/users/{id}: parameters: - $ref: '#/components/parameters/id' get: tags: [Users] summary: Pobierz użytkownika (admin) responses: '200': description: Użytkownik content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '404': { $ref: '#/components/responses/NotFound' } put: tags: [Users] summary: Zaktualizuj użytkownika (admin) requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UserInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '422': { $ref: '#/components/responses/ValidationError' } patch: tags: [Users] summary: Częściowo zaktualizuj użytkownika (admin) requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UserInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/UserResponse' } '422': { $ref: '#/components/responses/ValidationError' } delete: tags: [Users] summary: Usuń użytkownika (admin) responses: '204': { description: Usunięto } '400': { $ref: '#/components/responses/BadRequest' } /api/customers: get: tags: [Customers] summary: Lista klientów parameters: - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' - name: q in: query schema: { type: string, description: Szukanie po danych klienta } - name: type in: query schema: { type: string, enum: [individual, company] } - name: city in: query schema: { type: string } - name: country in: query schema: { type: string } - name: with_cars in: query schema: { type: boolean } responses: '200': description: Lista klientów content: application/json: schema: { $ref: '#/components/schemas/CustomerList' } post: tags: [Customers] summary: Dodaj klienta requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CustomerCreate' } responses: '201': description: Utworzono content: application/json: schema: { $ref: '#/components/schemas/CustomerResponse' } '422': { $ref: '#/components/responses/ValidationError' } /api/customers/{id}: parameters: - $ref: '#/components/parameters/id' get: tags: [Customers] summary: Szczegóły klienta wraz z samochodami responses: '200': description: Klient content: application/json: schema: { $ref: '#/components/schemas/CustomerResponse' } '404': { $ref: '#/components/responses/NotFound' } put: tags: [Customers] summary: Zaktualizuj klienta requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CustomerInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/CustomerResponse' } '422': { $ref: '#/components/responses/ValidationError' } patch: tags: [Customers] summary: Częściowo zaktualizuj klienta requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CustomerInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/CustomerResponse' } '422': { $ref: '#/components/responses/ValidationError' } delete: tags: [Customers] summary: Usuń klienta i jego samochody responses: '204': { description: Usunięto } '404': { $ref: '#/components/responses/NotFound' } /api/customers/{customer_id}/cars: parameters: - name: customer_id in: path required: true schema: { type: integer } - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' get: tags: [Cars] summary: Lista samochodów klienta responses: '200': description: Samochody klienta content: application/json: schema: { $ref: '#/components/schemas/CarList' } '404': { $ref: '#/components/responses/NotFound' } post: tags: [Cars] summary: Dodaj samochód klientowi requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/NestedCarInput' } responses: '201': description: Utworzono content: application/json: schema: { $ref: '#/components/schemas/CarResponse' } '422': { $ref: '#/components/responses/ValidationError' } /api/cars: get: tags: [Cars] summary: Lista samochodów parameters: - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/limit' - name: q in: query schema: { type: string, description: Szukanie po marce, modelu i numerach auta } - name: customer_id in: query schema: { type: integer } - name: make in: query schema: { type: string } - name: fuel_type in: query schema: { type: string, enum: [petrol, diesel, lpg, hybrid, electric] } - name: transmission in: query schema: { type: string, enum: [manual, automatic] } - name: year_from in: query schema: { type: integer } - name: year_to in: query schema: { type: integer } - name: with_customer in: query schema: { type: boolean } responses: '200': description: Lista samochodów content: application/json: schema: { $ref: '#/components/schemas/CarList' } post: tags: [Cars] summary: Dodaj samochód requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CarCreate' } responses: '201': description: Utworzono content: application/json: schema: { $ref: '#/components/schemas/CarResponse' } '422': { $ref: '#/components/responses/ValidationError' } /api/cars/{id}: parameters: - $ref: '#/components/parameters/id' get: tags: [Cars] summary: Szczegóły samochodu z właścicielem responses: '200': description: Samochód content: application/json: schema: { $ref: '#/components/schemas/CarResponse' } '404': { $ref: '#/components/responses/NotFound' } put: tags: [Cars] summary: Zaktualizuj samochód requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CarInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/CarResponse' } '422': { $ref: '#/components/responses/ValidationError' } patch: tags: [Cars] summary: Częściowo zaktualizuj samochód requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CarInput' } responses: '200': description: Zaktualizowano content: application/json: schema: { $ref: '#/components/schemas/CarResponse' } '422': { $ref: '#/components/responses/ValidationError' } delete: tags: [Cars] summary: Usuń samochód responses: '204': { description: Usunięto } '404': { $ref: '#/components/responses/NotFound' } components: securitySchemes: basicAuth: type: http scheme: basic parameters: id: name: id in: path required: true schema: { type: integer } page: name: page in: query schema: { type: integer, minimum: 1, default: 1 } limit: name: limit in: query schema: { type: integer, minimum: 1, maximum: 100, default: 20 } responses: Unauthorized: description: Brak lub nieprawidłowe dane HTTP Basic Auth content: application/json: schema: { $ref: '#/components/schemas/Error' } Forbidden: description: Wymagana rola admin content: application/json: schema: { $ref: '#/components/schemas/Error' } NotFound: description: Nie znaleziono rekordu content: application/json: schema: { $ref: '#/components/schemas/Error' } BadRequest: description: Niedozwolona operacja content: application/json: schema: { $ref: '#/components/schemas/Error' } ValidationError: description: Błędy walidacji content: application/json: schema: { $ref: '#/components/schemas/Error' } schemas: Error: type: object properties: success: { type: boolean, example: false } error: type: object properties: code: { type: integer } message: { type: string } fields: type: object additionalProperties: true Pagination: type: object properties: page: { type: integer } limit: { type: integer } total: { type: integer } pages: { type: integer } has_next_page: { type: boolean } has_prev_page: { type: boolean } sort: { type: string } direction: { type: string } User: type: object properties: id: { type: integer } login: { type: string } email: { type: string, format: email } first_name: { type: string } last_name: { type: string } role: { type: string, enum: [admin, user] } active: { type: boolean } last_login: { type: string, format: date-time, nullable: true } created: { type: string, format: date-time } modified: { type: string, format: date-time } UserInput: type: object properties: login: { type: string, maxLength: 255 } email: { type: string, format: email } password: { type: string, format: password, minLength: 8, maxLength: 72 } first_name: { type: string, maxLength: 100 } last_name: { type: string, maxLength: 100 } role: { type: string, enum: [admin, user] } active: { type: boolean } UserCreate: allOf: - $ref: '#/components/schemas/UserInput' - type: object required: [login, email, password, first_name, last_name] ProfileInput: type: object properties: email: { type: string, format: email } password: { type: string, format: password, minLength: 8 } first_name: { type: string } last_name: { type: string } UserResponse: type: object properties: success: { type: boolean } data: { $ref: '#/components/schemas/User' } UserList: type: object properties: success: { type: boolean } data: type: array items: { $ref: '#/components/schemas/User' } meta: { $ref: '#/components/schemas/Pagination' } CustomerInput: type: object properties: type: { type: string, enum: [individual, company], default: individual } first_name: { type: string, maxLength: 100 } last_name: { type: string, maxLength: 100 } email: { type: string, format: email } phone: { type: string, maxLength: 30 } company_name: { type: string, nullable: true } tax_id: { type: string, nullable: true } street: { type: string, nullable: true } city: { type: string, nullable: true } postal_code: { type: string, nullable: true } country: { type: string, default: PL } birth_date: { type: string, format: date, nullable: true } notes: { type: string, nullable: true } CustomerCreate: allOf: - $ref: '#/components/schemas/CustomerInput' - type: object required: [first_name, last_name, email, phone] Customer: allOf: - $ref: '#/components/schemas/CustomerInput' - type: object properties: id: { type: integer } created_by: { type: integer, nullable: true } created: { type: string, format: date-time } modified: { type: string, format: date-time } CustomerResponse: type: object properties: success: { type: boolean } data: allOf: - $ref: '#/components/schemas/Customer' - type: object properties: cars: type: array items: { $ref: '#/components/schemas/Car' } CustomerList: type: object properties: success: { type: boolean } data: type: array items: { $ref: '#/components/schemas/Customer' } meta: { $ref: '#/components/schemas/Pagination' } CarInput: type: object properties: customer_id: { type: integer } make: { type: string } model: { type: string } production_year: { type: integer } vin: { type: string, minLength: 17, maxLength: 17 } registration_number: { type: string } fuel_type: { type: string, enum: [petrol, diesel, lpg, hybrid, electric] } color: { type: string, nullable: true } engine_capacity: { type: integer, nullable: true } power_hp: { type: integer, nullable: true } transmission: { type: string, enum: [manual, automatic], nullable: true } mileage: { type: integer, nullable: true } notes: { type: string, nullable: true } CarCreate: allOf: - $ref: '#/components/schemas/CarInput' - type: object required: [customer_id, make, model, production_year, vin, registration_number, fuel_type] Car: allOf: - $ref: '#/components/schemas/CarInput' - type: object properties: id: { type: integer } created: { type: string, format: date-time } modified: { type: string, format: date-time } NestedCarInput: type: object required: [make, model, production_year, vin, registration_number, fuel_type] properties: make: { type: string } model: { type: string } production_year: { type: integer } vin: { type: string, minLength: 17, maxLength: 17 } registration_number: { type: string } fuel_type: { type: string, enum: [petrol, diesel, lpg, hybrid, electric] } color: { type: string, nullable: true } engine_capacity: { type: integer, nullable: true } power_hp: { type: integer, nullable: true } transmission: { type: string, enum: [manual, automatic], nullable: true } mileage: { type: integer, nullable: true } notes: { type: string, nullable: true } CarResponse: type: object properties: success: { type: boolean } data: allOf: - $ref: '#/components/schemas/Car' - type: object properties: customer: { $ref: '#/components/schemas/Customer' } CarList: type: object properties: success: { type: boolean } data: type: array items: { $ref: '#/components/schemas/Car' } meta: { $ref: '#/components/schemas/Pagination' }