feat: clients detail using auth
This commit is contained in:
parent
b9fea3ddd0
commit
d42cca2262
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <Add access token here>)
|
||||
// @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
|
||||
|
|
|
|||
|
|
@ -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("")
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error}</div>;
|
||||
if (!profile) return <div>No profile found</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{profile.name}</h1>
|
||||
<p>{profile.description}</p>
|
||||
<p>Type: {profile.clientType}</p>
|
||||
<p>Max Users: {profile.maxUsers}</p>
|
||||
{profile.logoUrl && <img src={profile.logoUrl} alt="Logo" />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## 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
|
||||
|
|
@ -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)",
|
||||
|
|
|
|||
|
|
@ -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)",
|
||||
|
|
|
|||
|
|
@ -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 <Add access token here>
|
||||
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 <Add access token here>
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue