diff --git a/app/module/clients/clients.module.go b/app/module/clients/clients.module.go index b7fca56..156181d 100644 --- a/app/module/clients/clients.module.go +++ b/app/module/clients/clients.module.go @@ -50,6 +50,7 @@ func (_i *ClientsRouter) RegisterClientsRoutes() { _i.App.Route("/clients", func(router fiber.Router) { router.Get("/", clientsController.All) router.Get("/:id", clientsController.Show) + router.Get("/profile", clientsController.ShowWithAuth) router.Post("/", clientsController.Save) router.Post("/with-user", clientsController.CreateClientWithUser) router.Put("/:id", clientsController.Update) diff --git a/app/module/clients/controller/clients.controller.go b/app/module/clients/controller/clients.controller.go index d2d421d..17fe170 100644 --- a/app/module/clients/controller/clients.controller.go +++ b/app/module/clients/controller/clients.controller.go @@ -21,6 +21,7 @@ type clientsController struct { type ClientsController interface { All(c *fiber.Ctx) error Show(c *fiber.Ctx) error + ShowWithAuth(c *fiber.Ctx) error Save(c *fiber.Ctx) error Update(c *fiber.Ctx) error UpdateWithAuth(c *fiber.Ctx) error @@ -140,6 +141,37 @@ func (_i *clientsController) Show(c *fiber.Ctx) error { }) } +// ShowWithAuth get Clients detail using auth token +// @Summary Get Clients detail with auth token +// @Description API for getting Clients detail using client ID from auth token +// @Tags Clients +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /clients/profile [get] +func (_i *clientsController) ShowWithAuth(c *fiber.Ctx) error { + // Get Authorization token from header + authToken := c.Get("Authorization") + _i.Log.Info().Str("authToken", authToken).Msg("") + + clientsData, err := _i.clientsService.ShowWithAuth(authToken) + if err != nil { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Messages: utilRes.Messages{err.Error()}, + }) + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Client profile successfully retrieved"}, + Data: clientsData, + }) +} + // Save create Clients // @Summary Create Clients // @Description API for create Clients diff --git a/app/module/clients/service/clients.service.go b/app/module/clients/service/clients.service.go index f194b12..f7d9fc0 100644 --- a/app/module/clients/service/clients.service.go +++ b/app/module/clients/service/clients.service.go @@ -34,6 +34,7 @@ type clientsService struct { type ClientsService interface { All(authToken string, req request.ClientsQueryRequest) (clients []*response.ClientsResponse, paging paginator.Pagination, err error) Show(id uuid.UUID) (clients *response.ClientsResponse, err error) + ShowWithAuth(authToken string) (clients *response.ClientsResponse, err error) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) Update(id uuid.UUID, req request.ClientsUpdateRequest) (err error) UpdateWithAuth(authToken string, req request.ClientsUpdateRequest) (err error) @@ -100,6 +101,31 @@ func (_i *clientsService) Show(id uuid.UUID) (clients *response.ClientsResponse, return mapper.ClientsResponseMapper(result), nil } +func (_i *clientsService) ShowWithAuth(authToken string) (clients *response.ClientsResponse, err error) { + // Extract clientId from authToken + user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if user == nil { + _i.Log.Error().Msg("User not found from auth token") + return nil, fmt.Errorf("user not found") + } + + if user.ClientId == nil { + _i.Log.Error().Msg("Client ID not found in user token") + return nil, fmt.Errorf("client ID not found in user token") + } + + clientId := *user.ClientId + _i.Log.Info().Str("clientId", clientId.String()).Msg("Getting client details with auth token") + + result, err := _i.Repo.FindOne(clientId) + if err != nil { + _i.Log.Error().Err(err).Str("clientId", clientId.String()).Msg("Failed to find client") + return nil, err + } + + return mapper.ClientsResponseMapper(result), nil +} + func (_i *clientsService) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) { _i.Log.Info().Interface("data", req).Msg("") diff --git a/docs/CLIENT_PROFILE_API.md b/docs/CLIENT_PROFILE_API.md new file mode 100644 index 0000000..0720d75 --- /dev/null +++ b/docs/CLIENT_PROFILE_API.md @@ -0,0 +1,215 @@ +# Client Profile API Documentation + +## Overview +API endpoint untuk mendapatkan detail client berdasarkan client ID yang diambil dari auth token, tanpa perlu menyertakan client ID sebagai path parameter. + +## Endpoint + +### Get Client Profile +**GET** `/clients/profile` + +#### Description +Mendapatkan detail client yang sedang login menggunakan client ID dari auth token. + +#### Parameters +- Tidak ada path parameter, client ID diambil dari auth token + +#### Headers +- **Authorization** (required): Bearer token untuk autentikasi user + +#### Response + +##### Success Response (200) +```json +{ + "success": true, + "messages": ["Client profile successfully retrieved"], + "data": { + "id": "123e4567-e89b-12d3-a456-426614174000", + "name": "Client Name", + "description": "Client description", + "clientType": "standalone", + "parentClientId": null, + "logoUrl": "https://example.com/logo.png", + "logoImagePath": "clients/logos/client-id/logo.png", + "address": "Jl. Example No. 123", + "phoneNumber": "+62-123-456-7890", + "website": "https://example.com", + "maxUsers": 100, + "maxStorage": 1073741824, + "settings": "{\"theme\": \"dark\"}", + "isActive": true, + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-01-01T00:00:00Z", + "parentClient": null, + "subClients": [] + } +} +``` + +##### Response Fields +- **id** (string): UUID client +- **name** (string): Nama client +- **description** (string): Deskripsi client +- **clientType** (string): Tipe client (`parent_client`, `sub_client`, `standalone`) +- **parentClientId** (string, nullable): ID parent client (untuk sub client) +- **logoUrl** (string, nullable): URL logo client +- **logoImagePath** (string, nullable): Path logo di MinIO storage +- **address** (string, nullable): Alamat client +- **phoneNumber** (string, nullable): Nomor telepon client +- **website** (string, nullable): Website resmi client +- **maxUsers** (integer, nullable): Batas maksimal user +- **maxStorage** (integer, nullable): Batas maksimal storage dalam bytes +- **settings** (string, nullable): JSON string untuk custom settings +- **isActive** (boolean, nullable): Status aktif client +- **createdAt** (string): Tanggal pembuatan +- **updatedAt** (string): Tanggal terakhir diupdate +- **parentClient** (object, nullable): Informasi parent client +- **subClients** (array): Daftar sub clients + +#### Error Responses + +##### 401 Unauthorized +```json +{ + "success": false, + "messages": ["user not found"] +} +``` + +##### 500 Internal Server Error +```json +{ + "success": false, + "messages": ["client ID not found in user token"] +} +``` + +## Usage Examples + +### cURL Example +```bash +curl -X GET "http://localhost:8080/clients/profile" \ + -H "Authorization: Bearer YOUR_TOKEN_HERE" +``` + +### JavaScript Example +```javascript +const getClientProfile = async () => { + try { + const response = await fetch('/clients/profile', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + const data = await response.json(); + + if (data.success) { + console.log('Client profile:', data.data); + return data.data; + } else { + console.error('Failed to get profile:', data.messages); + return null; + } + } catch (error) { + console.error('Error getting client profile:', error); + return null; + } +}; + +// Usage +const profile = await getClientProfile(); +if (profile) { + console.log('Client name:', profile.name); + console.log('Client type:', profile.clientType); + console.log('Max users:', profile.maxUsers); +} +``` + +### React Hook Example +```javascript +import { useState, useEffect } from 'react'; + +const useClientProfile = (token) => { + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchProfile = async () => { + try { + setLoading(true); + const response = await fetch('/clients/profile', { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + const data = await response.json(); + + if (data.success) { + setProfile(data.data); + setError(null); + } else { + setError(data.messages[0]); + } + } catch (err) { + setError('Failed to fetch client profile'); + } finally { + setLoading(false); + } + }; + + if (token) { + fetchProfile(); + } + }, [token]); + + return { profile, loading, error }; +}; + +// Usage in component +const ClientProfile = () => { + const { profile, loading, error } = useClientProfile(token); + + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + if (!profile) return
No profile found
; + + return ( +
+

{profile.name}

+

{profile.description}

+

Type: {profile.clientType}

+

Max Users: {profile.maxUsers}

+ {profile.logoUrl && Logo} +
+ ); +}; +``` + +## Use Cases +1. **Profile Display**: Menampilkan profil client di dashboard +2. **Settings Page**: Mengambil data client untuk halaman pengaturan +3. **Header/Navigation**: Menampilkan nama dan logo client di header +4. **Client Information**: Menampilkan informasi kontak client +5. **Resource Monitoring**: Mengecek batas user dan storage yang tersedia +6. **Logo Display**: Menampilkan logo client di berbagai halaman + +## Security Features +- **Authentication Required**: Harus menggunakan Bearer token yang valid +- **Client Isolation**: User hanya bisa melihat profil client mereka sendiri +- **Token Validation**: Client ID diambil dari token yang sudah diverifikasi +- **No Path Parameters**: Tidak ada client ID yang bisa diubah di URL + +## Notes +- Endpoint ini menggunakan middleware `UserMiddleware` untuk mengekstrak informasi user dari JWT token +- Client ID diambil dari `user.ClientId` dalam token +- Jika user tidak ditemukan atau client ID tidak ada dalam token, akan mengembalikan error +- Endpoint ini lebih aman daripada endpoint show dengan path parameter karena mencegah user melihat profil client lain +- Response mencakup semua informasi client termasuk parent client dan sub clients jika ada +- Field yang nullable akan berisi `null` jika tidak ada data diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index a035f01..4bba265 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -9800,6 +9800,113 @@ const docTemplate = `{ } } }, + "/clients/profile": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting Clients detail using client ID from auth token", + "tags": [ + "Clients" + ], + "summary": "Get Clients detail with auth token", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/clients/update": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Clients using client ID from auth token", + "tags": [ + "Clients" + ], + "summary": "update Clients with auth token", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ClientsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/clients/with-user": { "post": { "description": "API for creating a client and its admin user in a single request (Public endpoint)", diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 92a1d7f..a9c5193 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -9789,6 +9789,113 @@ } } }, + "/clients/profile": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting Clients detail using client ID from auth token", + "tags": [ + "Clients" + ], + "summary": "Get Clients detail with auth token", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/clients/update": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Clients using client ID from auth token", + "tags": [ + "Clients" + ], + "summary": "update Clients with auth token", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ClientsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/clients/with-user": { "post": { "description": "API for creating a client and its admin user in a single request (Public endpoint)", diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index c70ae32..ae7c2dd 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -8277,6 +8277,74 @@ paths: summary: Upload client logo tags: - Clients + /clients/profile: + get: + description: API for getting Clients detail using client ID from auth token + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get Clients detail with auth token + tags: + - Clients + /clients/update: + put: + description: API for update Clients using client ID from auth token + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ClientsUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update Clients with auth token + tags: + - Clients /clients/with-user: post: description: API for creating a client and its admin user in a single request