Skip to content

Commit 169e0f3

Browse files
authored
Merge pull request #1201 from andypols/fix-logout-baseurl
fix: Logout calls localhost in prod; standardise API base resolution
2 parents a15adf1 + 519b53d commit 169e0f3

6 files changed

Lines changed: 69 additions & 16 deletions

File tree

src/ui/apiBase.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const stripTrailingSlashes = (s: string) => s.replace(/\/+$/, '');
2+
3+
/**
4+
* The base URL for API requests.
5+
*
6+
* Uses the `VITE_API_URI` environment variable if set, otherwise defaults to the current origin.
7+
* @return {string} The base URL to use for API requests.
8+
*/
9+
export const API_BASE = process.env.VITE_API_URI
10+
? stripTrailingSlashes(process.env.VITE_API_URI)
11+
: '';

src/ui/components/Navbars/DashboardNavbarLinks.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import axios from 'axios';
1818
import { getAxiosConfig } from '../../services/auth';
1919
import { UserData } from '../../../types/models';
2020

21+
import { API_BASE } from '../../apiBase';
22+
2123
const useStyles = makeStyles(styles);
2224

2325
const DashboardNavbarLinks: React.FC = () => {
@@ -51,11 +53,7 @@ const DashboardNavbarLinks: React.FC = () => {
5153

5254
const logout = async () => {
5355
try {
54-
const { data } = await axios.post(
55-
`${process.env.VITE_API_URI || 'http://localhost:3000'}/api/auth/logout`,
56-
{},
57-
getAxiosConfig(),
58-
);
56+
const { data } = await axios.post(`${API_BASE}/api/auth/logout`, {}, getAxiosConfig());
5957

6058
if (!data.isAuth && !data.user) {
6159
setAuth(false);

src/ui/services/user.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import axios, { AxiosError, AxiosResponse } from 'axios';
22
import { getAxiosConfig, processAuthError } from './auth';
33
import { UserData } from '../../types/models';
44

5-
type SetStateCallback<T> = (value: T | ((prevValue: T) => T)) => void;
5+
import { API_BASE } from '../apiBase';
66

7-
const baseUrl = process.env.VITE_API_URI || location.origin;
7+
type SetStateCallback<T> = (value: T | ((prevValue: T) => T)) => void;
88

99
const getUser = async (
1010
setIsLoading?: SetStateCallback<boolean>,
@@ -13,9 +13,9 @@ const getUser = async (
1313
setIsError?: SetStateCallback<boolean>,
1414
id: string | null = null,
1515
): Promise<void> => {
16-
let url = `${baseUrl}/api/auth/profile`;
16+
let url = `${API_BASE}/api/auth/profile`;
1717
if (id) {
18-
url = `${baseUrl}/api/v1/user/${id}`;
18+
url = `${API_BASE}/api/v1/user/${id}`;
1919
}
2020
console.log(url);
2121

@@ -43,7 +43,7 @@ const getUsers = async (
4343
setErrorMessage: SetStateCallback<string>,
4444
query: Record<string, string> = {},
4545
): Promise<void> => {
46-
const url = new URL(`${baseUrl}/api/v1/user`);
46+
const url = new URL(`${API_BASE}/api/v1/user`);
4747
url.search = new URLSearchParams(query).toString();
4848

4949
setIsLoading(true);
@@ -70,7 +70,7 @@ const getUsers = async (
7070

7171
const updateUser = async (data: UserData): Promise<void> => {
7272
console.log(data);
73-
const url = new URL(`${baseUrl}/api/auth/gitAccount`);
73+
const url = new URL(`${API_BASE}/api/auth/gitAccount`);
7474

7575
try {
7676
await axios.post(url.toString(), data, getAxiosConfig());
@@ -89,7 +89,7 @@ const getUserLoggedIn = async (
8989
setIsError: SetStateCallback<boolean>,
9090
setAuth: SetStateCallback<boolean>,
9191
): Promise<void> => {
92-
const url = new URL(`${baseUrl}/api/auth/me`);
92+
const url = new URL(`${API_BASE}/api/auth/me`);
9393

9494
try {
9595
const response: AxiosResponse<UserData> = await axios(url.toString(), getAxiosConfig());

src/ui/utils.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import axios from 'axios';
22
import React from 'react';
33
import {
4-
SCMRepositoryMetadata,
4+
CommitData,
55
GitHubRepositoryMetadata,
66
GitLabRepositoryMetadata,
7-
CommitData,
7+
SCMRepositoryMetadata,
88
} from '../types/models';
99
import moment from 'moment';
1010

src/ui/views/Login/Login.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ import logo from '../../assets/img/git-proxy.png';
1515
import { Badge, CircularProgress, Snackbar } from '@material-ui/core';
1616
import { getCookie } from '../../utils';
1717
import { useAuth } from '../../auth/AuthProvider';
18+
import { API_BASE } from '../../apiBase';
1819

1920
interface LoginResponse {
2021
username: string;
2122
password: string;
2223
}
2324

24-
const loginUrl = `${process.env.VITE_API_URI}/api/auth/login`;
25+
const loginUrl = `${API_BASE}/api/auth/login`;
2526

2627
const Login: React.FC = () => {
2728
const navigate = useNavigate();
@@ -41,7 +42,7 @@ const Login: React.FC = () => {
4142
}
4243

4344
function handleOIDCLogin(): void {
44-
window.location.href = `${process.env.VITE_API_URI}/api/auth/oidc`;
45+
window.location.href = `${API_BASE}/api/auth/oidc`;
4546
}
4647

4748
function handleSubmit(event: FormEvent): void {

test/ui/apiBase.test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const { expect } = require('chai');
2+
3+
// Helper to reload the module fresh each time
4+
function loadApiBase() {
5+
delete require.cache[require.resolve('../../src/ui/apiBase')];
6+
return require('../../src/ui/apiBase');
7+
}
8+
9+
describe('apiBase', () => {
10+
let originalEnv;
11+
12+
beforeEach(() => {
13+
originalEnv = process.env.VITE_API_URI;
14+
delete process.env.VITE_API_URI;
15+
delete require.cache[require.resolve('../../src/ui/apiBase')];
16+
});
17+
18+
afterEach(() => {
19+
if (typeof originalEnv === 'undefined') {
20+
delete process.env.VITE_API_URI;
21+
} else {
22+
process.env.VITE_API_URI = originalEnv;
23+
}
24+
delete require.cache[require.resolve('../../src/ui/apiBase')];
25+
});
26+
27+
it('uses empty string when VITE_API_URI is not set', () => {
28+
const { API_BASE } = loadApiBase();
29+
expect(API_BASE).to.equal('');
30+
});
31+
32+
it('returns the exact value when no trailing slash', () => {
33+
process.env.VITE_API_URI = 'https://example.com';
34+
const { API_BASE } = loadApiBase();
35+
expect(API_BASE).to.equal('https://example.com');
36+
});
37+
38+
it('strips trailing slashes from VITE_API_URI', () => {
39+
process.env.VITE_API_URI = 'https://example.com////';
40+
const { API_BASE } = loadApiBase();
41+
expect(API_BASE).to.equal('https://example.com');
42+
});
43+
});

0 commit comments

Comments
 (0)