Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ next-env.d.ts

# claude files
.claude

# IntelliJ
.idea/
83 changes: 34 additions & 49 deletions src/lib/apiClient.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,40 @@
import axios, { AxiosInstance } from 'axios';
import axios from 'axios';
import Cookies from 'js-cookie';

let API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8081';

const createClient = (baseURL: string): AxiosInstance => {
const client = axios.create({
baseURL,
headers: {
'Content-Type': 'application/json',
},
});

// Add a request interceptor to include the auth token
client.interceptors.request.use(
(config) => {
const token = Cookies.get('tt_access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
// The base URL is now a constant, correctly pointing to the API Gateway.
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080/api/v1';

const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: { 'Content-Type': 'application/json' },
});

// Request interceptor (Your existing code is perfect)
apiClient.interceptors.request.use(
(config) => {
const token = Cookies.get('tt_access_token');
if (token) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
config.headers.Authorization = `Bearer ${token}`;
}
);

// Add a response interceptor to handle auth errors
client.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Clear token and redirect to login
Cookies.remove('tt_access_token');
if (typeof window !== 'undefined') {
window.location.href = '/auth/login';
}
return config;
},
(error) => Promise.reject(error)
);

// Response interceptor (Your existing code is perfect)
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
Cookies.remove('tt_access_token');
if (typeof window !== 'undefined') {
window.location.href = '/auth/login';
}
return Promise.reject(error);
}
);

return client;
};

let api = createClient(API_BASE);

export const setApiBaseUrl = (baseUrl: string) => {
API_BASE = baseUrl;
api = createClient(API_BASE);
};

export const getApiBaseUrl = () => API_BASE;
return Promise.reject(error);
}
);

export default api;
export default apiClient;
27 changes: 7 additions & 20 deletions src/services/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const TOKEN_COOKIE = 'tt_access_token';

export const authService = {
async login(payload: LoginRequest) {
const res = await api.post('/api/v1/auth/login', payload);
// api baseURL already includes '/api/v1'
const res = await api.post('/auth/login', payload);
// backend returns token in body
const token = res.data?.token || res.data?.accessToken || null;
console.log('Login response:', res.data);
Expand All @@ -26,23 +27,23 @@ export const authService = {


async register(payload: RegisterRequest) {
const res = await api.post('/api/v1/auth/register', payload);
const res = await api.post('/auth/register', payload);
return res.data;
},
async createEmployee(payload: CreateEmployeeRequest) {
const res = await api.post('/api/v1/auth/users/employee', payload);
const res = await api.post('/auth/users/employee', payload);
return res.data;
},
async createAdmin(payload: CreateAdminRequest) {
const res = await api.post('/api/v1/auth/users/admin', payload);
const res = await api.post('/auth/users/admin', payload);
return res.data;
},
async testEndpoint() {
const res = await api.get('/api/v1/auth/test');
const res = await api.get('/auth/test');
return res.data;
},
async healthCheck() {
const res = await api.get('/api/v1/auth/health');
const res = await api.get('/auth/health');
return res.data;
},
logout() {
Expand All @@ -53,18 +54,4 @@ export const authService = {
},
};

// Request interceptor to attach token
api.interceptors.request.use((config) => {
try {
const token = Cookies.get(TOKEN_COOKIE);
if (token && config.headers) {
config.headers['Authorization'] = `Bearer ${token}`;
}
} catch (err) {
// log to help debugging in production builds
console.warn('auth interceptor error', err);
}
return config;
});

export default authService;
26 changes: 15 additions & 11 deletions src/services/userService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,41 @@ import type {

export const userService = {
getCurrentProfile() {
return api.get('/api/v1/users/me');
// Correct Path: /users/me
return api.get('/users/me');
},
getAllUsers() {
return api.get('/api/v1/users');
// Correct Path: /users
return api.get('/users');
},
getUserByUsername(username: string) {
return api.get(`/api/v1/users/${encodeURIComponent(username)}`);
// Correct Path: /users/{username}
return api.get(`/users/${encodeURIComponent(username)}`);
},
updateUser(username: string, payload: UpdateUserRequest) {
return api.put(`/api/v1/users/${encodeURIComponent(username)}`, payload);
return api.put(`/users/${encodeURIComponent(username)}`, payload);
},
deleteUser(username: string) {
return api.delete(`/api/v1/users/${encodeURIComponent(username)}`);
return api.delete(`/users/${encodeURIComponent(username)}`);
},
enableUser(username: string) {
return api.post(`/api/v1/users/${encodeURIComponent(username)}/enable`);
return api.post(`/users/${encodeURIComponent(username)}/enable`);
},
disableUser(username: string) {
return api.post(`/api/v1/users/${encodeURIComponent(username)}/disable`);
return api.post(`/users/${encodeURIComponent(username)}/disable`);
},
unlockUser(username: string) {
return api.post(`/api/v1/users/${encodeURIComponent(username)}/unlock`);
return api.post(`/users/${encodeURIComponent(username)}/unlock`);
},
manageUserRole(username: string, payload: RoleAssignmentRequest) {
return api.post(`/api/v1/users/${encodeURIComponent(username)}/roles`, payload);
return api.post(`/users/${encodeURIComponent(username)}/roles`, payload);
},
resetUserPassword(username: string, payload: ResetPasswordRequest) {
return api.post(`/api/v1/users/${encodeURIComponent(username)}/reset-password`, payload);
return api.post(`/users/${encodeURIComponent(username)}/reset-password`, payload);
},
changeCurrentUserPassword(payload: ChangePasswordRequest) {
return api.post('/api/v1/users/me/change-password', payload);
// Correct Path: /users/me/change-password
return api.post('/users/me/change-password', payload);
},
};

Expand Down