From e9936eb35deb06f12cbc1f8d6673f500666b4a21 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 28 Jan 2025 15:39:30 +0100 Subject: [PATCH] config and base request methods --- src/config.ts | 26 ++++++ src/http.ts | 86 +++++++++++++++++++ src/index.ts | 14 +++ src/requeststore/admin/club/memberRequests.ts | 33 +++++++ src/requeststore/requeststore.ts | 14 +++ 5 files changed, 173 insertions(+) create mode 100644 src/config.ts create mode 100644 src/http.ts create mode 100644 src/index.ts create mode 100644 src/requeststore/admin/club/memberRequests.ts create mode 100644 src/requeststore/requeststore.ts diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..d3a8035 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,26 @@ +export default class Config { + private server_adress = ""; + private webapi_token = ""; + private access_token = ""; + + constructor({ serverAdress, webapiToken }: { serverAdress: string; webapiToken: string }) { + this.server_adress = serverAdress; + this.webapi_token = webapiToken; + } + + public get serverAdress() { + return this.server_adress; + } + + public get webapiToken() { + return this.webapi_token; + } + + public get accessToken() { + return this.access_token; + } + + public setAccessToken(token: string) { + this.access_token = token; + } +} diff --git a/src/http.ts b/src/http.ts new file mode 100644 index 0000000..dfcf708 --- /dev/null +++ b/src/http.ts @@ -0,0 +1,86 @@ +import axios, { AxiosInstance } from "axios"; +import { WebApiClient } from "."; + +export default class http { + public http: AxiosInstance; + private host: WebApiClient; + + constructor(host: WebApiClient) { + this.host = host; + this.http = axios.create({ + baseURL: this.host.accessToken + "/api", + headers: { + "Cache-Control": "no-cache", + Pragma: "no-cache", + Expires: "0", + }, + }); + + this.setRequestInterceptor(); + this.setResponseInterceptor(); + } + + private setRequestInterceptor() { + this.http.interceptors.request.use( + (config) => { + if (config.headers && config.headers.Authorization == "") { + config.headers.Authorization = `Bearer ${this.host.accessToken}`; + } + + return config; + }, + (error) => { + return Promise.reject(error); + } + ); + } + + private setResponseInterceptor() { + this.http.interceptors.response.use( + (response) => { + return response; + }, + async (error) => { + if (!error.config.url.includes("/admin")) { + return Promise.reject(error); + } + + const originalRequest = error.config; + + // Handle token expiration and retry the request with a refreshed token + if (error.response && error.response.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + return await this.refreshToken() + .then(() => { + return this.http(originalRequest); + }) + .catch(() => {}); + } + + return Promise.reject(error); + } + ); + } + + private refreshToken(): Promise { + return new Promise(async (resolve, reject) => { + await this.http + .get(`/webapi/retrieve`, { + headers: { + Authorization: `Bearer ${this.host.webapiToken}`, + }, + }) + .then(async (response) => { + const { accessToken } = response.data; + + this.host.setAccessToken(accessToken); + + resolve(); + }) + .catch((error) => { + console.error("Error refreshing webapi token:", error); + reject("failed token retrieve"); + }); + }); + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3ab58f3 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,14 @@ +import Config from "./config"; +import http from "./http"; + +export class WebApiClient extends Config { + private httpInstance: http; + constructor({ serverAdress, webapiToken }: { serverAdress: string; webapiToken: string }) { + super({ serverAdress, webapiToken }); + this.httpInstance = new http(this); + } + + get http() { + return this.httpInstance.http; + } +} diff --git a/src/requeststore/admin/club/memberRequests.ts b/src/requeststore/admin/club/memberRequests.ts new file mode 100644 index 0000000..970cee7 --- /dev/null +++ b/src/requeststore/admin/club/memberRequests.ts @@ -0,0 +1,33 @@ +import { AxiosInstance, AxiosResponse } from "axios"; +import { + MemberViewModel, + CreateMemberViewModel, + UpdateMemberViewModel, +} from "../../../viewmodels/admin/club/member/member.models"; +import { RequestDefinition, RequestData } from "../../requeststore"; + +abstract class IMemberRequests { + static getAllMembers: RequestDefinition; + static getMemberById: RequestDefinition<{ id: number }, void, MemberViewModel>; + static createMember: RequestDefinition; + static updateMember: RequestDefinition<{ id: number }, UpdateMemberViewModel, void>; + static deleteMember: RequestDefinition<{ id: number }, void, void>; +} + +export default abstract class MemberRequests extends IMemberRequests { + static async getAllMembers(http: AxiosInstance): Promise> { + return await http.get("/admin/member"); + } + static async getMemberById(http: AxiosInstance, p: RequestData<{ id: number }, void>) { + return await http.get(`/admin/member/${p.params.id}`); + } + static async createMember(http: AxiosInstance, p: RequestData) { + return await http.post("/admin/member", p.body); + } + static async updateMember(http: AxiosInstance, p: RequestData<{ id: number }, UpdateMemberViewModel>) { + return await http.post(`/admin/member/${p.params.id}`, p.body); + } + static async deleteMember(http: AxiosInstance, p: RequestData<{ id: number }, void>) { + return await http.post(`/admin/member/${p.params.id}`); + } +} diff --git a/src/requeststore/requeststore.ts b/src/requeststore/requeststore.ts new file mode 100644 index 0000000..e0429ce --- /dev/null +++ b/src/requeststore/requeststore.ts @@ -0,0 +1,14 @@ +import { AxiosInstance, AxiosResponse } from "axios"; + +export type RequestData = PARAMS extends void + ? BODY extends void + ? {} + : { body: BODY } + : BODY extends void + ? { params: PARAMS } + : { params: PARAMS; body: BODY }; + +export type RequestDefinition = ( + http: AxiosInstance, + params: RequestData +) => Promise>;