From af43d8625de8fb6944b1e113c6b57679645ae5d6 Mon Sep 17 00:00:00 2001 From: Timotej Date: Thu, 29 Jan 2026 13:59:28 +0100 Subject: [PATCH 1/5] swagger api documentation --- docs/openapi.yaml | 244 ++++++++++++++++++++++++++++++ resources/views/swagger.blade.php | 22 +++ routes/api.php | 15 ++ 3 files changed, 281 insertions(+) create mode 100644 docs/openapi.yaml create mode 100644 resources/views/swagger.blade.php diff --git a/docs/openapi.yaml b/docs/openapi.yaml new file mode 100644 index 0000000..76410b5 --- /dev/null +++ b/docs/openapi.yaml @@ -0,0 +1,244 @@ +openapi: 3.0.3 +info: + title: CloudShopt User Service API + version: 1.0.0 +tags: + - name: Health + - name: Diagnostics + - name: Auth + +paths: + /healthz: + get: + tags: [Health] + summary: Health check + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/StatusOkBool" + + /info: + get: + tags: [Diagnostics] + summary: Service info (sha, time) + responses: + "200": + description: Info + content: + application/json: + schema: + $ref: "#/components/schemas/InfoResponse" + + /database: + get: + tags: [Diagnostics] + summary: Database connectivity check + responses: + "200": + description: DB OK + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseOkResponse" + "500": + description: DB connection failed + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseFailResponse" + + /auth/register: + post: + tags: [Auth] + summary: Register user + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RegisterRequest" + responses: + "201": + description: Registered + content: + application/json: + schema: + $ref: "#/components/schemas/AuthResponse" + "422": + $ref: "#/components/responses/ValidationError" + + /auth/login: + post: + tags: [Auth] + summary: Login user + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/LoginRequest" + responses: + "200": + description: Logged in + content: + application/json: + schema: + $ref: "#/components/schemas/AuthResponse" + "422": + $ref: "#/components/responses/ValidationError" + + /me: + get: + tags: [Auth] + summary: Get current user (JWT) + security: + - bearerAuth: [] + responses: + "200": + description: User + content: + application/json: + schema: + $ref: "#/components/schemas/MeResponse" + "401": + $ref: "#/components/responses/Unauthorized" + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + responses: + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorMessage" + examples: + missingToken: + value: { message: "Missing Bearer token" } + invalid: + value: { message: "Invalid or expired token" } + + ValidationError: + description: Validation error + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationError" + + schemas: + ErrorMessage: + type: object + required: [message] + properties: + message: { type: string } + + ValidationError: + type: object + required: [message, errors] + properties: + message: { type: string } + errors: + type: object + additionalProperties: + type: array + items: { type: string } + + StatusOkBool: + type: object + required: [ok] + properties: + ok: + type: boolean + example: true + + InfoResponse: + type: object + required: [ok, service, sha, time] + properties: + ok: { type: boolean, example: true } + service: { type: string, example: user-service } + sha: + type: string + nullable: true + example: 0.0.1 + time: + type: string + format: date-time + example: "2026-01-29T10:15:30.123Z" + + DatabaseOkResponse: + type: object + required: [ok, db, time] + properties: + ok: { type: boolean, example: true } + db: + type: object + required: [connection, database, ping_ms] + properties: + connection: { type: string, example: mysql } + database: { type: string, example: cloudshopt_users_dev } + ping_ms: { type: number, format: float, example: 1.25 } + time: + type: string + format: date-time + example: "2026-01-29T10:15:30.123Z" + + DatabaseFailResponse: + type: object + required: [ok, error, message] + properties: + ok: { type: boolean, example: false } + error: { type: string, example: DB connection failed } + message: + type: string + nullable: true + example: "SQLSTATE[HY000] ..." + + User: + type: object + required: [id, name, email] + properties: + id: { type: integer, example: 2 } + name: { type: string, example: Timotej } + email: { type: string, format: email, example: timotej@test.com } + + RegisterRequest: + type: object + required: [name, email, password, password_confirmation] + properties: + name: { type: string } + email: { type: string, format: email } + password: { type: string, format: password } + password_confirmation: { type: string, format: password } + + LoginRequest: + type: object + required: [email, password] + properties: + email: { type: string, format: email } + password: { type: string, format: password } + + AuthResponse: + type: object + required: [token, token_type, expires_in, user] + properties: + token: { type: string } + token_type: { type: string, example: Bearer } + expires_in: { type: integer, example: 3600 } + user: + $ref: "#/components/schemas/User" + + MeResponse: + type: object + required: [user] + properties: + user: + $ref: "#/components/schemas/User" \ No newline at end of file diff --git a/resources/views/swagger.blade.php b/resources/views/swagger.blade.php new file mode 100644 index 0000000..038dc67 --- /dev/null +++ b/resources/views/swagger.blade.php @@ -0,0 +1,22 @@ + + + + + + API Docs + + + +
+ + + + + \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index b04584c..96ea698 100644 --- a/routes/api.php +++ b/routes/api.php @@ -4,6 +4,21 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Route; +Route::get('/openapi.yaml', function () { + $path = base_path('docs/openapi.yaml'); + + abort_unless(file_exists($path), 404, 'openapi.yaml not found'); + + return response()->file($path, [ + 'Content-Type' => 'application/yaml; charset=utf-8', + 'Cache-Control' => 'no-store', + ]); +}); + +Route::get('/docs', function () { + return response()->view('swagger'); +}); + Route::get('/info', function () { return response()->json([ 'ok' => true, From 636c62529ded1558a4ea6db17b84327ffe5b87b9 Mon Sep 17 00:00:00 2001 From: Timotej Date: Thu, 29 Jan 2026 14:40:58 +0100 Subject: [PATCH 2/5] docs --- README.md | 67 +++++++++---------------------------------------------- 1 file changed, 10 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index c7aaa81..7170d19 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,23 @@ -# User service +## user-service +- **Purpose:** User registration/login and JWT issuance. +- **Base path:** `/api/users` -## Create users database + user on mysql server +### Create database ``` kubectl -n cloudshopt exec -it cloudshopt-mysql-0 -- bash -``` -``` +# mysql -u root -prootpass + CREATE DATABASE cloudshopt_users CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE USER 'users'@'%' IDENTIFIED BY 'userspass'; +CREATE USER 'users'@'%' IDENTIFIED BY 'CHANGE_ME_PASSWORD'; GRANT ALL PRIVILEGES ON cloudshopt_users.* TO 'users'@'%'; FLUSH PRIVILEGES; ``` -Ustvari še bazo za *dev* okolje -``` -CREATE DATABASE cloudshopt_users_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE USER 'users_dev'@'%' IDENTIFIED BY 'userspass'; -GRANT ALL PRIVILEGES ON cloudshopt_users_dev.* TO 'users_dev'@'%'; -FLUSH PRIVILEGES; -``` - -## Crete external secrets for prod and dev -prod: -``` -kubectl -n cloudshopt create secret generic user-service-secrets \ - --from-literal=DB_PASSWORD="userspass" \ - --from-literal=REDIS_PASSWORD="redispass" \ - --dry-run=client -o yaml | kubectl apply -f - -``` - -dev: -``` -kubectl -n cloudshopt-dev create secret generic user-service-secrets \ - --from-literal=DB_PASSWORD="userspass" \ - --from-literal=REDIS_PASSWORD="redispass" \ - --dry-run=client -o yaml | kubectl apply -f - -``` - -check for secrets: -``` -kubectl get secret -n cloudshopt user-service-secrets -kubectl get secret -n cloudshopt-dev user-service-secrets -``` +### Migrations -## Install user-service for prod and dev -prod: ``` -helm upgrade --install user-service ./helm/user-service \ --n cloudshopt \ --f helm/user-service/values.yaml -``` - -dev: -``` -helm upgrade --install user-service-dev ./helm/user-service \ --n cloudshopt-dev \ --f helm/user-service/values-dev.yaml -``` - - - -## Migrations - -run migrations: -``` -kubectl exec -n cloudshopt-dev -it deploy/user-service-dev -c app -- sh - +kubectl exec -n cloudshopt -it deploy/user-service -c app -- sh # php artisan migrate ``` + From 501d9dd7cce1de635fa97b85dd483c89687cc91e Mon Sep 17 00:00:00 2001 From: Timotej Date: Thu, 29 Jan 2026 15:05:39 +0100 Subject: [PATCH 3/5] test health endpoint --- routes/web.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/web.php b/routes/web.php index 3ab76a1..bf1eb31 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,4 +2,4 @@ use Illuminate\Support\Facades\Route; -Route::get('/healthz', fn () => response('ok', 200)); +//Route::get('/healthz', fn () => response('ok', 200)); From 9caa4b59ec1646aba5b669d6dd244baed674fc66 Mon Sep 17 00:00:00 2001 From: Timotej Date: Thu, 29 Jan 2026 15:20:16 +0100 Subject: [PATCH 4/5] fix health --- routes/web.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/web.php b/routes/web.php index bf1eb31..3ab76a1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,4 +2,4 @@ use Illuminate\Support\Facades\Route; -//Route::get('/healthz', fn () => response('ok', 200)); +Route::get('/healthz', fn () => response('ok', 200)); From 0dc3ded1d473b5cfdc9c7bf588c1a51850185c92 Mon Sep 17 00:00:00 2001 From: Timotej Date: Thu, 29 Jan 2026 15:24:05 +0100 Subject: [PATCH 5/5] fix --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index 96ea698..5323af8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -57,4 +57,4 @@ Route::post('/login', [AuthController::class, 'login']); }); -Route::middleware('jwt')->get('/me', [AuthController::class, 'me']); \ No newline at end of file +Route::middleware('jwt')->get('/me', [AuthController::class, 'me']);