2021-01-21 11:36:18 +08:00
|
|
|
import { Context } from '@nuxt/types';
|
|
|
|
import { Inject } from '@nuxt/types/app';
|
2021-01-31 10:00:11 +08:00
|
|
|
import { AxiosError, AxiosPromise } from 'axios';
|
2021-01-21 11:36:18 +08:00
|
|
|
import { ApiSession } from 'hangar-api';
|
|
|
|
import { NuxtAxiosInstance } from '@nuxtjs/axios';
|
|
|
|
import { NuxtCookies } from 'cookie-universal-nuxt';
|
|
|
|
import { Store } from 'vuex';
|
|
|
|
import { ApiSessionType } from '~/types/enums';
|
|
|
|
|
|
|
|
const createApi = ($axios: NuxtAxiosInstance, $cookies: NuxtCookies, store: Store<any>) => {
|
|
|
|
class API {
|
|
|
|
getSession(): Promise<string> {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let session: ApiSession;
|
|
|
|
const date = new Date();
|
|
|
|
date.setTime(date.getTime() + 60000);
|
|
|
|
|
|
|
|
if (store.state.auth.authenticated) {
|
|
|
|
session = $cookies.get('api_session');
|
|
|
|
if (typeof session === 'undefined' || (!isNaN(new Date(session.expires).getTime()) && new Date(session.expires) < date)) {
|
|
|
|
return $axios
|
2021-01-22 11:11:24 +08:00
|
|
|
.post<ApiSession>('/api/v1/authenticate/user', {}, { headers: { 'Content-Type': 'application/json' } })
|
|
|
|
.then(({ data }) => {
|
|
|
|
if (data.type !== ApiSessionType.USER) {
|
2021-01-21 11:36:18 +08:00
|
|
|
reject(new Error('Expected user session from user authentication'));
|
|
|
|
} else {
|
|
|
|
$cookies.set('api_session', JSON.stringify(data), {
|
|
|
|
path: '/',
|
2021-01-22 13:18:09 +08:00
|
|
|
expires: new Date(data.expires),
|
2021-01-22 11:11:24 +08:00
|
|
|
secure: true,
|
2021-01-21 11:36:18 +08:00
|
|
|
});
|
|
|
|
resolve(data.session);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
2021-01-22 11:11:24 +08:00
|
|
|
store.commit('auth/SET_AUTHED', false);
|
2021-01-21 11:36:18 +08:00
|
|
|
reject(error);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
resolve(session.session);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
session = $cookies.get('public_api_session');
|
|
|
|
if (typeof session === 'undefined' || (!isNaN(new Date(session.expires).getTime()) && new Date(session.expires) < date)) {
|
|
|
|
$axios
|
|
|
|
.post<ApiSession>('/api/v1/authenticate', {}, { headers: { 'Content-Type': 'application/json' } })
|
|
|
|
.then(({ data }) => {
|
|
|
|
if (data.type !== ApiSessionType.PUBLIC) {
|
|
|
|
reject(new Error('Expected public session from public authentication'));
|
|
|
|
} else {
|
|
|
|
$cookies.set('public_api_session', JSON.stringify(data), {
|
|
|
|
path: '/',
|
2021-01-22 13:18:09 +08:00
|
|
|
expires: new Date(data.expires),
|
2021-01-22 11:11:24 +08:00
|
|
|
secure: true,
|
2021-01-21 11:36:18 +08:00
|
|
|
});
|
|
|
|
resolve(data.session);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
reject(error);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
resolve(session.session);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-03 07:47:15 +08:00
|
|
|
getToken(fetch: boolean = true): Promise<string | null> {
|
|
|
|
if (store.state.auth.token) {
|
|
|
|
// TODO check expiry
|
|
|
|
console.log('token exists');
|
|
|
|
return Promise.resolve(store.state.auth.token);
|
|
|
|
} else if (fetch) {
|
|
|
|
console.log('fetching token');
|
|
|
|
return this.refreshToken();
|
|
|
|
} else {
|
|
|
|
return Promise.resolve(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshToken(): Promise<string | null> {
|
|
|
|
return new Promise<string | null>((resolve) => {
|
|
|
|
return $axios
|
|
|
|
.get<string>('/refresh')
|
|
|
|
.then((value) => {
|
|
|
|
store.commit('auth/SET_TOKEN', value.data);
|
|
|
|
resolve(value.data);
|
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
resolve(null);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-01-21 11:36:18 +08:00
|
|
|
invalidateSession(): void {
|
2021-01-22 13:18:09 +08:00
|
|
|
store.commit('auth/SET_AUTHED', false);
|
|
|
|
store.commit('auth/SET_USER', null);
|
|
|
|
$cookies.remove('api_session', {
|
|
|
|
path: '/',
|
|
|
|
});
|
|
|
|
$cookies.remove('public_api_session', {
|
|
|
|
path: '/',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-03 07:47:15 +08:00
|
|
|
requestInternalWithToken<T>(url: string, token: string | null, method: 'get' | 'post' = 'get', data: object = {}): Promise<T> {
|
|
|
|
return new Promise<T>((resolve, reject) => {
|
|
|
|
const headers = token ? { Authorization: `HangarAuth ${token}` } : {};
|
|
|
|
return ($axios({
|
|
|
|
method,
|
|
|
|
url: '/api/internal/' + url,
|
|
|
|
headers,
|
|
|
|
data,
|
|
|
|
}) as AxiosPromise<T>)
|
|
|
|
.then(({ data }) => resolve(data))
|
|
|
|
.catch((error: AxiosError) => {
|
|
|
|
reject(error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
requestInternal<T>(url: string, authed: boolean = true, method: 'get' | 'post' = 'get', data: object = {}): Promise<T> {
|
|
|
|
return this.getToken(authed).then((token) => {
|
|
|
|
return this.requestInternalWithToken<T>(url, token, method, data);
|
|
|
|
});
|
2021-01-21 11:36:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
request<T>(url: string, method: 'get' | 'post' = 'get', data: object = {}): Promise<T> {
|
2021-01-22 13:18:09 +08:00
|
|
|
return this._request(`v1/${url}`, method, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _request<T>(url: string, method: 'get' | 'post', data: object): Promise<T> {
|
2021-01-21 11:36:18 +08:00
|
|
|
return new Promise<T>((resolve, reject) => {
|
2021-01-22 11:11:24 +08:00
|
|
|
return this.getSession()
|
|
|
|
.then((session) => {
|
|
|
|
return ($axios({
|
|
|
|
method,
|
2021-01-22 13:18:09 +08:00
|
|
|
url: '/api/' + url,
|
2021-01-22 11:11:24 +08:00
|
|
|
headers: { Authorization: 'HangarApi session="' + session + '"' },
|
|
|
|
data,
|
|
|
|
}) as AxiosPromise<T>)
|
|
|
|
.then(({ data }) => resolve(data))
|
2021-01-22 15:27:42 +08:00
|
|
|
.catch((error: AxiosError) => {
|
|
|
|
reject(error);
|
|
|
|
// console.log(error);
|
|
|
|
// if (error.response && (error.response.error === 'Api session expired' || error.response.error === 'Invalid session')) {
|
|
|
|
// // This should never happen but just in case we catch it and invalidate the session to definitely get a new one
|
|
|
|
// this.invalidateSession();
|
|
|
|
// this.request<T>(url, method, data)
|
|
|
|
// .then((data) => {
|
|
|
|
// resolve(data);
|
|
|
|
// })
|
|
|
|
// .catch((error) => {
|
|
|
|
// reject(error);
|
|
|
|
// });
|
|
|
|
// } else {
|
|
|
|
// reject(error.response.statusText);
|
|
|
|
// }
|
2021-01-22 11:11:24 +08:00
|
|
|
});
|
|
|
|
})
|
2021-01-22 15:27:42 +08:00
|
|
|
.catch((reason) => {
|
|
|
|
console.log(reason);
|
|
|
|
// TODO error popup here
|
|
|
|
});
|
2021-01-21 11:36:18 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new API();
|
|
|
|
};
|
|
|
|
|
|
|
|
type apiType = ReturnType<typeof createApi>;
|
|
|
|
|
|
|
|
declare module 'vue/types/vue' {
|
|
|
|
interface Vue {
|
|
|
|
$api: apiType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare module '@nuxt/types' {
|
|
|
|
interface NuxtAppOptions {
|
|
|
|
$api: apiType;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Context {
|
|
|
|
$api: apiType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
declare module 'vuex/types/index' {
|
2021-01-31 10:00:11 +08:00
|
|
|
// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
|
2021-01-21 11:36:18 +08:00
|
|
|
interface Store<S> {
|
|
|
|
$api: apiType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default ({ $axios, app: { $cookies }, store }: Context, inject: Inject) => {
|
|
|
|
inject('api', createApi($axios, $cookies, store));
|
|
|
|
};
|