Skip to content

Commit 0fb2783

Browse files
committed
fix: redirect user to login if a 401 happens in the admin API
1 parent b671d0e commit 0fb2783

File tree

17 files changed

+465
-163
lines changed

17 files changed

+465
-163
lines changed

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ It is built using HTML5, CSS, TypeScript, and PHP and supports various databases
5454
- TypeScript code in watch mode: pnpm test:watch
5555
- TypeScript linting: pnpm lint
5656
- TypeScript code formatting: pnpm lint:fix
57+
- TypeScript errors have to be fixed before committing code.
5758

5859
## Building
5960

phpmyfaq/admin/assets/src/api/attachment.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,35 @@
1313
* @since 2024-05-01
1414
*/
1515

16+
import { fetchJson } from './fetch-wrapper';
1617
import { Response } from '../interfaces';
1718

1819
export const deleteAttachments = async (attachmentId: string, csrfToken: string): Promise<Response> => {
19-
const response = await fetch('./api/content/attachments', {
20+
return await fetchJson('./api/content/attachments', {
2021
method: 'DELETE',
2122
headers: {
2223
Accept: 'application/json, text/plain, */*',
2324
'Content-Type': 'application/json',
2425
},
2526
body: JSON.stringify({ attId: attachmentId, csrf: csrfToken }),
2627
});
27-
28-
return await response.json();
2928
};
3029

3130
export const refreshAttachments = async (attachmentId: string, csrfToken: string): Promise<Response> => {
32-
const response = await fetch('./api/content/attachments/refresh', {
31+
return await fetchJson('./api/content/attachments/refresh', {
3332
method: 'POST',
3433
headers: {
3534
Accept: 'application/json, text/plain, */*',
3635
'Content-Type': 'application/json',
3736
},
3837
body: JSON.stringify({ attId: attachmentId, csrf: csrfToken }),
3938
});
40-
41-
return await response.json();
4239
};
4340

4441
export const uploadAttachments = async (formData: FormData): Promise<Response> => {
45-
const response = await fetch('./api/content/attachments/upload', {
42+
return await fetchJson('./api/content/attachments/upload', {
4643
method: 'POST',
4744
cache: 'no-cache',
4845
body: formData,
4946
});
50-
51-
return await response.json();
5247
};

phpmyfaq/admin/assets/src/api/category.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
* @link https://www.phpmyfaq.de
1313
* @since 2023-12-28
1414
*/
15+
import { fetchJson } from './fetch-wrapper';
1516
import { CategoryTranslations, Response } from '../interfaces';
1617

1718
export const fetchCategoryTranslations = async (categoryId: string): Promise<CategoryTranslations> => {
18-
const response = await fetch(`./api/category/translations/${categoryId}`, {
19+
return await fetchJson(`./api/category/translations/${categoryId}`, {
1920
method: 'GET',
2021
cache: 'no-cache',
2122
headers: {
@@ -24,16 +25,14 @@ export const fetchCategoryTranslations = async (categoryId: string): Promise<Cat
2425
redirect: 'follow',
2526
referrerPolicy: 'no-referrer',
2627
});
27-
28-
return await response.json();
2928
};
3029

3130
export const deleteCategory = async (
3231
categoryId: string,
3332
language: string,
3433
csrfToken: string
3534
): Promise<Response | undefined> => {
36-
const response = await fetch(`./api/category/delete`, {
35+
return await fetchJson(`./api/category/delete`, {
3736
method: 'DELETE',
3837
cache: 'no-cache',
3938
headers: {
@@ -47,16 +46,14 @@ export const deleteCategory = async (
4746
redirect: 'follow',
4847
referrerPolicy: 'no-referrer',
4948
});
50-
51-
return await response.json();
5249
};
5350

5451
export const setCategoryTree = async (
5552
categoryTree: unknown,
5653
categoryId: string,
5754
csrfToken: string
5855
): Promise<Response | undefined> => {
59-
const response = await fetch('./api/category/update-order', {
56+
return await fetchJson('./api/category/update-order', {
6057
method: 'POST',
6158
cache: 'no-cache',
6259
headers: {
@@ -70,6 +67,4 @@ export const setCategoryTree = async (
7067
redirect: 'follow',
7168
referrerPolicy: 'no-referrer',
7269
});
73-
74-
return await response.json();
7570
};

phpmyfaq/admin/assets/src/api/faqs.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,20 @@ describe('fetchFaqsByAutocomplete', () => {
3838
expect(global.fetch).toHaveBeenCalled();
3939
});
4040

41-
test('should throw an error if the network response is not ok', async () => {
41+
test('should return error JSON for non-ok response', async () => {
42+
const mockError = { error: 'Server error', status: 500 };
4243
global.fetch = vi.fn(() =>
4344
Promise.resolve({
4445
status: 500,
46+
json: () => Promise.resolve(mockError),
4547
} as Response)
4648
);
4749

4850
const searchTerm = 'faq';
4951
const csrfToken = 'csrfToken';
5052

51-
await expect(fetchFaqsByAutocomplete(searchTerm, csrfToken)).rejects.toThrow('Network response was not ok.');
53+
const result = await fetchFaqsByAutocomplete(searchTerm, csrfToken);
54+
expect(result).toEqual(mockError);
5255
});
5356
});
5457

@@ -71,18 +74,21 @@ describe('deleteFaq', () => {
7174
expect(global.fetch).toHaveBeenCalled();
7275
});
7376

74-
test('should throw an error if the network response is not ok', async () => {
77+
test('should return error JSON for non-ok response', async () => {
78+
const mockError = { error: 'Failed to delete', status: 500 };
7579
global.fetch = vi.fn(() =>
7680
Promise.resolve({
7781
status: 500,
82+
json: () => Promise.resolve(mockError),
7883
} as Response)
7984
);
8085

8186
const faqId = '123';
8287
const faqLanguage = 'en';
8388
const csrfToken = 'csrfToken';
8489

85-
await expect(deleteFaq(faqId, faqLanguage, csrfToken)).rejects.toThrow('Network response was not ok.');
90+
const result = await deleteFaq(faqId, faqLanguage, csrfToken);
91+
expect(result).toEqual(mockError);
8692
});
8793
});
8894

phpmyfaq/admin/assets/src/api/faqs.ts

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* @since 2023-12-27
1414
*/
1515

16+
import { fetchJson } from './fetch-wrapper';
1617
import { Response } from '../interfaces';
1718
import { FaqList } from '../interfaces';
1819

@@ -37,7 +38,7 @@ export const fetchAllFaqsByCategory = async (
3738
if (onlyNew) {
3839
url.searchParams.set('only-new', onlyNew as unknown as string);
3940
}
40-
const response = await fetch(url.toString(), {
41+
return await fetchJson(url.toString(), {
4142
method: 'GET',
4243
cache: 'no-cache',
4344
headers: {
@@ -46,12 +47,10 @@ export const fetchAllFaqsByCategory = async (
4647
redirect: 'follow',
4748
referrerPolicy: 'no-referrer',
4849
});
49-
50-
return await response.json();
5150
};
5251

5352
export const fetchFaqsByAutocomplete = async (searchTerm: string, csrfToken: string): Promise<Response | undefined> => {
54-
const response = await fetch(`./api/faq/search`, {
53+
return await fetchJson(`./api/faq/search`, {
5554
method: 'POST',
5655
headers: {
5756
Accept: 'application/json, text/plain, */*',
@@ -62,16 +61,10 @@ export const fetchFaqsByAutocomplete = async (searchTerm: string, csrfToken: str
6261
csrf: csrfToken,
6362
}),
6463
});
65-
66-
if (response.status === 200) {
67-
return await response.json();
68-
}
69-
70-
throw new Error('Network response was not ok.');
7164
};
7265

7366
export const deleteFaq = async (faqId: string, faqLanguage: string, token: string): Promise<Response | undefined> => {
74-
const response = await fetch('./api/faq/delete', {
67+
return await fetchJson('./api/faq/delete', {
7568
method: 'DELETE',
7669
headers: {
7770
Accept: 'application/json, text/plain, */*',
@@ -83,16 +76,10 @@ export const deleteFaq = async (faqId: string, faqLanguage: string, token: strin
8376
faqLanguage: faqLanguage,
8477
}),
8578
});
86-
87-
if (response.status === 200) {
88-
return await response.json();
89-
}
90-
91-
throw new Error('Network response was not ok.');
9279
};
9380

9481
export const create = async (formData: unknown): Promise<Response | undefined> => {
95-
const response = await fetch('./api/faq/create', {
82+
return await fetchJson('./api/faq/create', {
9683
method: 'POST',
9784
headers: {
9885
Accept: 'application/json, text/plain, */*',
@@ -102,12 +89,10 @@ export const create = async (formData: unknown): Promise<Response | undefined> =
10289
data: formData,
10390
}),
10491
});
105-
106-
return await response.json();
10792
};
10893

10994
export const update = async (formData: unknown): Promise<Response | undefined> => {
110-
const response = await fetch('./api/faq/update', {
95+
return await fetchJson('./api/faq/update', {
11196
method: 'PUT',
11297
headers: {
11398
Accept: 'application/json, text/plain, */*',
@@ -117,6 +102,4 @@ export const update = async (formData: unknown): Promise<Response | undefined> =
117102
data: formData,
118103
}),
119104
});
120-
121-
return await response.json();
122105
};

0 commit comments

Comments
 (0)