feat: clients detail using auth

This commit is contained in:
hanif salafi 2025-10-12 13:23:22 +07:00
parent b9fea3ddd0
commit d42cca2262
7 changed files with 556 additions and 0 deletions

View File

@ -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)

View File

@ -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

View File

@ -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("")

215
docs/CLIENT_PROFILE_API.md Normal file
View File

@ -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

View File

@ -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)",

View File

@ -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)",

View File

@ -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