mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-15 04:32:13 +00:00
feat: refactor api
This commit is contained in:
parent
f21a8bf784
commit
ba08e0b112
4 changed files with 65 additions and 57 deletions
|
@ -43,4 +43,7 @@ export class UserPreferences {
|
|||
|
||||
@Column("text", { default: "" })
|
||||
refreshToken: string;
|
||||
|
||||
@Column("int", { default: 0 })
|
||||
tokenExpirationTimestamp: number;
|
||||
}
|
||||
|
|
|
@ -12,3 +12,8 @@ export const downloadSourceSchema = z.object({
|
|||
})
|
||||
),
|
||||
});
|
||||
|
||||
export const refreshTokenSchema = z.object({
|
||||
accessToken: z.string(),
|
||||
expiresIn: z.number(),
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ const loadState = async (userPreferences: UserPreferences | null) => {
|
|||
if (userPreferences?.realDebridApiToken)
|
||||
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
|
||||
|
||||
HydraApi.createInstance();
|
||||
HydraApi.setupApi();
|
||||
|
||||
const [nextQueueItem] = await downloadQueueRepository.find({
|
||||
order: {
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import { refreshTokenSchema } from "@main/events/helpers/validators";
|
||||
import { userPreferencesRepository } from "@main/repository";
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
export class HydraApi {
|
||||
private static instance: AxiosInstance;
|
||||
|
||||
static authToken = "";
|
||||
static refreshToken = "";
|
||||
private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5;
|
||||
|
||||
static async createInstance() {
|
||||
private static userAuth = {
|
||||
authToken: "",
|
||||
refreshToken: "",
|
||||
expirationTimestamp: 0,
|
||||
};
|
||||
|
||||
static async setupApi() {
|
||||
this.instance = axios.create({
|
||||
baseURL: import.meta.env.MAIN_VITE_API_URL,
|
||||
});
|
||||
|
@ -16,72 +22,66 @@ export class HydraApi {
|
|||
where: { id: 1 },
|
||||
});
|
||||
|
||||
this.authToken = userPreferences?.accessToken ?? "";
|
||||
this.refreshToken = userPreferences?.refreshToken ?? "";
|
||||
this.userAuth = {
|
||||
authToken: userPreferences?.accessToken ?? "",
|
||||
refreshToken: userPreferences?.refreshToken ?? "",
|
||||
expirationTimestamp: userPreferences?.tokenExpirationTimestamp ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
this.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
config.headers.Authorization = `Bearer ${this.authToken}`;
|
||||
return config;
|
||||
private static async revalidateAccessTokenIfExpired() {
|
||||
const now = new Date();
|
||||
if (this.userAuth.expirationTimestamp > now.getTime()) {
|
||||
const response = await this.instance.post(`/auth/refresh`, {
|
||||
refreshToken: this.userAuth.refreshToken,
|
||||
});
|
||||
|
||||
const { accessToken, expiresIn } = refreshTokenSchema.parse(
|
||||
response.data
|
||||
);
|
||||
|
||||
const tokenExpirationTimestamp =
|
||||
now.getTime() + expiresIn - this.EXPIRATION_OFFSET_IN_MS;
|
||||
|
||||
this.userAuth.authToken = accessToken;
|
||||
this.userAuth.expirationTimestamp = tokenExpirationTimestamp;
|
||||
|
||||
userPreferencesRepository.upsert(
|
||||
{
|
||||
id: 1,
|
||||
accessToken,
|
||||
tokenExpirationTimestamp,
|
||||
},
|
||||
["id"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static getAxiosConfig() {
|
||||
return {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.userAuth.authToken}`,
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
this.instance.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
if (error.response.status === 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true;
|
||||
const refreshToken = this.refreshToken;
|
||||
|
||||
if (!refreshToken) return error;
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${import.meta.env.MAIN_VITE_API_URL}/auth/refresh`,
|
||||
{ refreshToken }
|
||||
);
|
||||
const newAccessToken = response.data.accessToken;
|
||||
this.authToken = newAccessToken;
|
||||
|
||||
userPreferencesRepository.upsert(
|
||||
{
|
||||
id: 1,
|
||||
accessToken: newAccessToken,
|
||||
},
|
||||
["id"]
|
||||
);
|
||||
|
||||
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
||||
return axios(originalRequest); //recall Api with new token
|
||||
} catch (err) {
|
||||
this.authToken = "";
|
||||
this.refreshToken = "";
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
static async get(url: string) {
|
||||
return this.instance.get(url);
|
||||
this.revalidateAccessTokenIfExpired();
|
||||
return this.instance.get(url, this.getAxiosConfig());
|
||||
}
|
||||
|
||||
static async post(url: string, data?: any) {
|
||||
return this.instance.post(url, data);
|
||||
this.revalidateAccessTokenIfExpired();
|
||||
return this.instance.post(url, data, this.getAxiosConfig());
|
||||
}
|
||||
|
||||
static async put(url, data?: any) {
|
||||
return this.instance.put(url, data);
|
||||
this.revalidateAccessTokenIfExpired();
|
||||
return this.instance.put(url, data, this.getAxiosConfig());
|
||||
}
|
||||
|
||||
static async patch(url, data?: any) {
|
||||
return this.instance.patch(url, data);
|
||||
this.revalidateAccessTokenIfExpired();
|
||||
return this.instance.patch(url, data, this.getAxiosConfig());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue