More relaxed parsing of api url, fix race condition when putting heartbeats back into queue
This commit is contained in:
@@ -52,9 +52,6 @@ export default function Options(): JSX.Element {
|
||||
const handleSubmit = async () => {
|
||||
if (state.loading) return;
|
||||
setState((oldState) => ({ ...oldState, loading: true }));
|
||||
if (state.apiUrl.endsWith('/')) {
|
||||
state.apiUrl = state.apiUrl.slice(0, -1);
|
||||
}
|
||||
await saveSettings({
|
||||
allowList: state.allowList.filter((item) => !!item.trim()),
|
||||
apiKey: state.apiKey,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { changeExtensionStatus } from '../utils/changeExtensionStatus';
|
||||
import getDomainFromUrl, { getDomain } from '../utils/getDomainFromUrl';
|
||||
import { getOperatingSystem, IS_EDGE, IS_FIREFOX } from '../utils/operatingSystem';
|
||||
import { getSettings, Settings } from '../utils/settings';
|
||||
import { getApiUrl } from '../utils/user';
|
||||
|
||||
import config, { ExtensionStatus } from '../config/config';
|
||||
import { EntityType, Heartbeat, HeartbeatsBulkResponse } from '../types/heartbeats';
|
||||
@@ -166,7 +167,6 @@ class WakaTimeCore {
|
||||
async sendHeartbeats(): Promise<void> {
|
||||
const settings = await browser.storage.sync.get({
|
||||
apiKey: config.apiKey,
|
||||
apiUrl: config.apiUrl,
|
||||
heartbeatApiEndPoint: config.heartbeatApiEndPoint,
|
||||
hostname: '',
|
||||
});
|
||||
@@ -175,16 +175,8 @@ class WakaTimeCore {
|
||||
return;
|
||||
}
|
||||
|
||||
const heartbeats = (await (await this.db()).getAll(config.queueName, undefined, 25)) as
|
||||
| Heartbeat[]
|
||||
| undefined;
|
||||
if (!heartbeats || heartbeats.length === 0) return;
|
||||
|
||||
await Promise.all(
|
||||
heartbeats.map(async (heartbeat) => {
|
||||
return (await this.db()).delete(config.queueName, heartbeat.id);
|
||||
}),
|
||||
);
|
||||
const heartbeats = await this.getHeartbeatsFromQueue();
|
||||
if (heartbeats.length === 0) return;
|
||||
|
||||
const userAgent = await this.getUserAgent();
|
||||
|
||||
@@ -204,7 +196,15 @@ class WakaTimeCore {
|
||||
};
|
||||
}
|
||||
|
||||
const url = `${settings.apiUrl}${settings.heartbeatApiEndPoint}?api_key=${settings.apiKey}`;
|
||||
try {
|
||||
await getApiUrl();
|
||||
} catch (err: unknown) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = await getApiUrl();
|
||||
const url = `${apiUrl}${settings.heartbeatApiEndPoint}?api_key=${settings.apiKey}`;
|
||||
const response = await fetch(url, request);
|
||||
if (response.status === 401) {
|
||||
await this.putHeartbeatsBackInQueue(heartbeats);
|
||||
@@ -246,10 +246,38 @@ class WakaTimeCore {
|
||||
}
|
||||
}
|
||||
|
||||
async putHeartbeatsBackInQueue(heartbeats: Heartbeat[]): Promise<void> {
|
||||
async getHeartbeatsFromQueue(): Promise<Heartbeat[]> {
|
||||
const tx = (await this.db()).transaction(config.queueName, 'readwrite');
|
||||
|
||||
const heartbeats = (await tx.store.getAll(undefined, 25)) as Heartbeat[] | undefined;
|
||||
if (!heartbeats || heartbeats.length === 0) return [];
|
||||
|
||||
await Promise.all(
|
||||
heartbeats.map(async (heartbeat) => (await this.db()).add(config.queueName, heartbeat)),
|
||||
heartbeats.map(async (heartbeat) => {
|
||||
return tx.store.delete(heartbeat.id);
|
||||
}),
|
||||
);
|
||||
|
||||
await tx.done;
|
||||
|
||||
return heartbeats;
|
||||
}
|
||||
|
||||
async putHeartbeatsBackInQueue(heartbeats: Heartbeat[]): Promise<void> {
|
||||
await Promise.all(heartbeats.map(async (heartbeat) => this.putHeartbeatBackInQueue(heartbeat)));
|
||||
}
|
||||
|
||||
async putHeartbeatBackInQueue(heartbeat: Heartbeat, tries = 0): Promise<void> {
|
||||
try {
|
||||
await (await this.db()).add(config.queueName, heartbeat);
|
||||
} catch (err: unknown) {
|
||||
if (tries < 10) {
|
||||
return await this.putHeartbeatBackInQueue(heartbeat, tries + 1);
|
||||
}
|
||||
console.error(err);
|
||||
console.error(`Unable to add heartbeat back into queue: ${heartbeat.id}`);
|
||||
console.error(JSON.stringify(heartbeat));
|
||||
}
|
||||
}
|
||||
|
||||
async getUserAgent(): Promise<string> {
|
||||
|
||||
@@ -3,6 +3,7 @@ import axios, { AxiosResponse } from 'axios';
|
||||
import browser from 'webextension-polyfill';
|
||||
import config from '../config/config';
|
||||
import { CurrentUser, User, UserPayload } from '../types/user';
|
||||
import { getApiUrl } from '../utils/user';
|
||||
|
||||
interface setUserAction {
|
||||
payload: User | undefined;
|
||||
@@ -16,11 +17,11 @@ export const fetchCurrentUser = createAsyncThunk<User, string>(
|
||||
`[${name}]`,
|
||||
async (api_key = '') => {
|
||||
const items = await browser.storage.sync.get({
|
||||
apiUrl: config.apiUrl,
|
||||
currentUserApiEndPoint: config.currentUserApiEndPoint,
|
||||
});
|
||||
const apiUrl = await getApiUrl();
|
||||
const userPayload: AxiosResponse<UserPayload> = await axios.get(
|
||||
`${items.apiUrl}${items.currentUserApiEndPoint}`,
|
||||
`${apiUrl}${items.currentUserApiEndPoint}`,
|
||||
{
|
||||
params: { api_key },
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AnyAction, Dispatch } from '@reduxjs/toolkit';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import browser from 'webextension-polyfill';
|
||||
|
||||
import moment from 'moment';
|
||||
import config from '../config/config';
|
||||
@@ -9,6 +10,30 @@ import { GrandTotal, Summaries } from '../types/summaries';
|
||||
import { ApiKeyPayload, AxiosUserResponse, User } from '../types/user';
|
||||
import changeExtensionState from './changeExtensionStatus';
|
||||
|
||||
export const getApiUrl = async () => {
|
||||
const settings = await browser.storage.sync.get({
|
||||
apiUrl: config.apiUrl,
|
||||
});
|
||||
let apiUrl = (settings.apiUrl as string) || config.apiUrl;
|
||||
const suffixes = [
|
||||
'/',
|
||||
'.bulk',
|
||||
'.bulk',
|
||||
'/users/current/heartbeats',
|
||||
'/heartbeats',
|
||||
'/heartbeat',
|
||||
];
|
||||
for (const suffix of suffixes) {
|
||||
if (apiUrl.endsWith(suffix)) {
|
||||
apiUrl = apiUrl.slice(0, -suffix.length);
|
||||
}
|
||||
}
|
||||
if (!apiUrl.endsWith('/api/v1')) {
|
||||
apiUrl = `${apiUrl}/api/v1`;
|
||||
}
|
||||
return apiUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the user is logged in.
|
||||
*
|
||||
@@ -16,11 +41,11 @@ import changeExtensionState from './changeExtensionStatus';
|
||||
*/
|
||||
const checkAuth = async (api_key = ''): Promise<User> => {
|
||||
const items = await browser.storage.sync.get({
|
||||
apiUrl: config.apiUrl,
|
||||
currentUserApiEndPoint: config.currentUserApiEndPoint,
|
||||
});
|
||||
const apiUrl = await getApiUrl();
|
||||
const userPayload: AxiosResponse<AxiosUserResponse> = await axios.get(
|
||||
`${items.apiUrl}${items.currentUserApiEndPoint}`,
|
||||
`${apiUrl}${items.currentUserApiEndPoint}`,
|
||||
{ params: { api_key } },
|
||||
);
|
||||
return userPayload.data.data;
|
||||
@@ -54,12 +79,11 @@ export const logUserIn = async (apiKey: string): Promise<void> => {
|
||||
const fetchApiKey = async (): Promise<string> => {
|
||||
try {
|
||||
const items = await browser.storage.sync.get({
|
||||
apiUrl: config.apiUrl,
|
||||
currentUserApiEndPoint: config.currentUserApiEndPoint,
|
||||
});
|
||||
|
||||
const apiUrl = await getApiUrl();
|
||||
const apiKeyResponse: AxiosResponse<ApiKeyPayload> = await axios.post(
|
||||
`${items.apiUrl}${items.currentUserApiEndPoint}/get_api_key`,
|
||||
`${apiUrl}${items.currentUserApiEndPoint}/get_api_key`,
|
||||
);
|
||||
return apiKeyResponse.data.data.api_key;
|
||||
} catch (err: unknown) {
|
||||
@@ -69,13 +93,12 @@ const fetchApiKey = async (): Promise<string> => {
|
||||
|
||||
const getTotalTimeLoggedToday = async (api_key = ''): Promise<GrandTotal> => {
|
||||
const items = await browser.storage.sync.get({
|
||||
apiUrl: config.apiUrl,
|
||||
summariesApiEndPoint: config.summariesApiEndPoint,
|
||||
});
|
||||
|
||||
const apiUrl = await getApiUrl();
|
||||
const today = moment().format('YYYY-MM-DD');
|
||||
const summariesAxiosPayload: AxiosResponse<Summaries> = await axios.get(
|
||||
`${items.apiUrl}${items.summariesApiEndPoint}`,
|
||||
`${apiUrl}${items.summariesApiEndPoint}`,
|
||||
{
|
||||
params: {
|
||||
api_key,
|
||||
|
||||
Reference in New Issue
Block a user