Skip to content

Commit

Permalink
Replace axios with fetch (#44)
Browse files Browse the repository at this point in the history
* Replace axios with fetch

* Remove bold font from options link in userguide

* Use record
  • Loading branch information
dedoussis authored Dec 18, 2023
1 parent e2de6ab commit c25df12
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 283 deletions.
289 changes: 100 additions & 189 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "icloud-hide-my-email-browser-extension",
"version": "1.2.0",
"version": "1.2.1",
"description": "Cross-browser extension enabling the usage of iCloud's Hide My Email service.",
"license": "MIT",
"author": "dedoussis",
Expand All @@ -24,21 +24,19 @@
"@fortawesome/free-regular-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@vespaiach/axios-fetch-adapter": "^0.3.0",
"axios": "^0.27.2",
"fuse.js": "^6.6.2",
"lodash.isequal": "^4.5.0",
"lodash.startcase": "^4.4.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/chrome": "^0.0.243",
"@types/lodash.isequal": "^4.5.6",
"@types/lodash.startcase": "^4.4.7",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/react-dom": "^18.2.18",
"@types/uuid": "^8.3.4",
"@types/webextension-polyfill": "^0.9.2",
"@typescript-eslint/eslint-plugin": "^5.47.1",
Expand Down
158 changes: 83 additions & 75 deletions src/iCloudClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from 'axios';
import isEqual from 'lodash.isequal';

type BaseUrlConfig = {
Expand All @@ -8,10 +7,8 @@ type BaseUrlConfig = {
};

export type ICloudClientSessionData = {
webservices: {
[k: string]: { url: string; status: string };
};
headers: { [k: string]: string };
webservices: Record<string, { url: string; status: string }>;
headers: Record<string, string>;
};

export const EMPTY_SESSION_DATA = {
Expand Down Expand Up @@ -79,29 +76,35 @@ class ICloudClient {
setup: 'https://setup.icloud.com/setup/ws/1',
};

public readonly requester;

constructor(
private readonly session: ICloudClientSession = new ICloudClientSession(),
private readonly axiosConfig?: AxiosRequestConfig,
readonly baseUrls: BaseUrlConfig = ICloudClient.DEFAULT_BASE_URL_CONFIG
) {
this.requester = axios.create(this.axiosConfig);
this.requester.interceptors.request.use(
this.prepareRequest.bind(this),
(error) => Promise.reject(error),
{ synchronous: true }
);
}
) {}

public async request(
method: 'GET' | 'POST',
url: string,
options: {
headers?: Record<string, string>;
data?: Record<string, unknown>;
} = {}
): Promise<unknown> {
const { headers = {}, data = undefined } = options;

private prepareRequest(config: AxiosRequestConfig) {
ICloudClientSession.HEADERS.forEach((headerKey: string) => {
const sessionVal = this.session.data.headers[headerKey];
if (sessionVal !== undefined) {
(config.headers as AxiosRequestHeaders)[headerKey] = sessionVal;
if (sessionVal !== undefined && headers[headerKey] === undefined) {
headers[headerKey] = sessionVal;
}
});
return config;

const response = await fetch(url, {
method,
headers,
body: data !== undefined ? JSON.stringify(data) : undefined,
});

return response.json();
}

public async populateAndPersistSessionHeaders(
Expand Down Expand Up @@ -142,9 +145,12 @@ class ICloudClient {
);
}

const {
data: { webservices },
} = await this.requester.post(`${this.baseUrls.setup}/validate`);
const { webservices } = (await this.request(
'POST',
`${this.baseUrls.setup}/validate`
)) as {
webservices: ICloudClientSessionData['webservices'];
};

if (populateAndPersistSessionWebservices && webservices) {
this.session.data.webservices = webservices;
Expand All @@ -154,12 +160,12 @@ class ICloudClient {

public async signOut(trust = false): Promise<void> {
if (this.authenticated) {
await this.requester
.post(`${this.baseUrls.setup}/logout`, {
await this.request('POST', `${this.baseUrls.setup}/logout`, {
data: {
trustBrowsers: trust,
allBrowsers: trust,
})
.catch(console.debug);
},
}).catch(console.debug);
}

await this.resetSession();
Expand All @@ -185,6 +191,14 @@ export type ListHmeResult = {
forwardToEmails: string[];
};

type PremiumMailSettingsResponse<T = unknown> = {
success: boolean;
result: T;
error?: {
errorMessage: string;
};
};

export class ClientAuthenticationError extends Error {}

export class GenerateHmeException extends Error {}
Expand All @@ -209,22 +223,24 @@ export class PremiumMailSettings {
}

async listHme(): Promise<ListHmeResult> {
const response = await this.client.requester.get(
const { result } = (await this.client.request(
'GET',
`${this.v2BaseUrl}/hme/list`
);
return response.data.result;
)) as PremiumMailSettingsResponse<ListHmeResult>;
return result;
}

async generateHme(): Promise<string> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/generate`
);
)) as PremiumMailSettingsResponse<{ hme: string }>;

if (!response.data.success) {
throw new GenerateHmeException(response.data.error.errorMessage);
if (!response.success) {
throw new GenerateHmeException(response.error?.errorMessage);
}

return response.data.result.hme;
return response.result.hme;
}

async reserveHme(
Expand All @@ -234,87 +250,79 @@ export class PremiumMailSettings {
| string
| undefined = 'Generated through the iCloud Hide My Email browser extension'
): Promise<HmeEmail> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/reserve`,
{
hme,
label,
note,
}
);
{ data: { hme, label, note } }
)) as PremiumMailSettingsResponse<{ hme: HmeEmail }>;

if (!response.data.success) {
throw new ReserveHmeException(response.data.error.errorMessage);
if (!response.success) {
throw new ReserveHmeException(response.error?.errorMessage);
}

return response.data.result.hme;
return response.result.hme;
}

async updateHmeMetadata(
anonymousId: string,
label: string,
note?: string
): Promise<void> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/updateMetaData`,
{
anonymousId,
label,
note,
}
);
{ data: { anonymousId, label, note } }
)) as PremiumMailSettingsResponse;

if (!response.data.success) {
if (!response.success) {
throw new UpdateHmeMetadataException('Failed to update HME metadata');
}
}

async deactivateHme(anonymousId: string): Promise<void> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/deactivate`,
{
anonymousId,
}
);
{ data: { anonymousId } }
)) as PremiumMailSettingsResponse;

if (!response.data.success) {
if (!response.success) {
throw new DeactivateHmeException('Failed to deactivate HME');
}
}

async reactivateHme(anonymousId: string): Promise<void> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/reactivate`,
{
anonymousId,
}
);
{ data: { anonymousId } }
)) as PremiumMailSettingsResponse;

if (!response.data.success) {
if (!response.success) {
throw new ReactivateHmeException('Failed to reactivate HME');
}
}

async deleteHme(anonymousId: string): Promise<void> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/delete`,
{
anonymousId,
}
);
{ data: { anonymousId } }
)) as PremiumMailSettingsResponse;

if (!response.data.success) {
if (!response.success) {
throw new DeleteHmeException('Failed to delete HME');
}
}

async updateForwardToHme(forwardToEmail: string): Promise<void> {
const response = await this.client.requester.post(
const response = (await this.client.request(
'POST',
`${this.baseUrl}/hme/updateForwardTo`,
{ forwardToEmail }
);
{ data: { forwardToEmail } }
)) as PremiumMailSettingsResponse;

if (!response.data.success) {
if (!response.success) {
throw new UpdateFwdToHmeException(
'Failed to update the Forward To email.'
);
Expand Down
3 changes: 1 addition & 2 deletions src/pages/Background/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'regenerator-runtime/runtime.js';
import fetchAdapter from '@vespaiach/axios-fetch-adapter';
import {
getBrowserStorageValue,
POPUP_STATE_STORAGE_KEYS,
Expand Down Expand Up @@ -42,7 +41,7 @@ const getClient = async (withTokenValidation = true): Promise<ICloudClient> => {
await setBrowserStorageValue(SESSION_DATA_STORAGE_KEYS, data)
);

const client = new ICloudClient(clientSession, { adapter: fetchAdapter });
const client = new ICloudClient(clientSession);

if (withTokenValidation && client.authenticated) {
try {
Expand Down
6 changes: 4 additions & 2 deletions src/pages/Options/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import { render } from 'react-dom';
import { createRoot } from 'react-dom/client';

import Options from './Options';
import './index.css';

render(<Options />, window.document.querySelector('#app-container'));
const container = document.getElementById('app-container') as HTMLElement;
const root = createRoot(container);
root.render(<Options />);
6 changes: 4 additions & 2 deletions src/pages/Popup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import { render } from 'react-dom';
import { createRoot } from 'react-dom/client';

import Popup from './Popup';
import './index.css';

render(<Popup />, window.document.querySelector('#app-container'));
const container = document.getElementById('app-container') as HTMLElement;
const root = createRoot(container);
root.render(<Popup />);
2 changes: 1 addition & 1 deletion src/pages/Userguide/Userguide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ const UsageInstructions = () => {
it in the{' '}
<a
href="./options.html"
className="text-sky-400 font-medium hover:text-sky-500"
className="text-sky-400 hover:text-sky-500"
target="_blank"
rel="noreferrer"
>
Expand Down
11 changes: 5 additions & 6 deletions src/pages/Userguide/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React from 'react';
import { render } from 'react-dom';
import { createRoot } from 'react-dom/client';

import Userguide from './Userguide';
import './index.css';
import contentScript from '../Content/script';

render(
<Userguide />,
window.document.querySelector('#app-container'),
contentScript
);
const container = document.getElementById('app-container') as HTMLElement;
const root = createRoot(container);
root.render(<Userguide />);
requestIdleCallback(contentScript);

0 comments on commit c25df12

Please sign in to comment.