Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: login flow for the app (#246) #248

Merged
merged 16 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,020 changes: 1,109 additions & 1,911 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
"@casl/vue": "^2.2.0",
"@hotwax/app-version-info": "^1.0.0",
"@hotwax/apps-theme": "^1.2.6",
"@hotwax/dxp-components": "1.12.1",
"@hotwax/oms-api": "^1.10.0",
"@ionic/core": "7.6.0",
"@ionic/vue": "7.6.0",
"@ionic/vue-router": "7.6.0",
"axios": "^0.21.1",
"axios-cache-adapter": "^2.7.3",
"http-status-codes": "^2.1.4",
"boolean-parser": "0.0.2",
"boon-js": "^2.0.3",
"core-js": "^3.6.5",
Expand Down
36 changes: 2 additions & 34 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { useRouter } from 'vue-router';
import { mapGetters, useStore } from 'vuex';
import { Settings } from 'luxon'
import Menu from '@/components/Menu.vue';
import { initialise, resetConfig } from '@/adapter'

export default defineComponent({
name: 'App',
Expand All @@ -29,17 +28,13 @@ export default defineComponent({
},
data() {
return {
loader: null as any,
maxAge: process.env.VUE_APP_CACHE_MAX_AGE ? parseInt(process.env.VUE_APP_CACHE_MAX_AGE) : 0,
alias: JSON.parse(process.env.VUE_APP_ALIAS) as any,
defaultAlias: process.env.VUE_APP_DEFAULT_ALIAS,
loader: null as any
};
},
computed: {
...mapGetters({
instanceUrl: 'user/getInstanceUrl',
userProfile: 'user/getUserProfile',
eComStore: 'user/getCurrentEComStore',
userToken: 'user/getUserToken',
isAuthenticated: 'user/isAuthenticated',
})
Expand All @@ -64,30 +59,8 @@ export default defineComponent({
this.loader.dismiss();
this.loader = null as any;
}
},
async unauthorized() {
// Mark the user as unauthorised, this will help in not making the logout api call in actions
this.store.dispatch("user/logout", { isUserUnauthorised: true });
const redirectUrl = window.location.origin + '/login';
window.location.href = `${process.env.VUE_APP_LOGIN_URL}?redirectUrl=${redirectUrl}`;
}
},
created() {
initialise({
token: this.userToken,
instanceUrl: this.instanceUrl,
cacheMaxAge: this.maxAge,
events: {
unauthorised: this.unauthorized,
responseError: () => {
setTimeout(() => this.dismissLoader(), 100);
},
queueTask: (payload: any) => {
emitter.emit("queueTask", payload);
}
}
})
},
async mounted() {
this.loader = await loadingController
.create({
Expand All @@ -97,21 +70,16 @@ export default defineComponent({
});
emitter.on('presentLoader', this.presentLoader);
emitter.on('dismissLoader', this.dismissLoader);

// Handles case when user resumes or reloads the app
// Luxon timezzone should be set with the user's selected timezone
if (this.userProfile && this.userProfile.userTimeZone) {
Settings.defaultZone = this.userProfile.userTimeZone;
}
if (this.isAuthenticated && !this.instanceUrl && !process.env.VUE_APP_BASE_URL) {
// If the current URL is available in alias show it for consistency
const defaultAliasInstanceUrl = this.alias[this.defaultAlias];
this.store.dispatch("user/setUserInstanceUrl", defaultAliasInstanceUrl);
}
},
unmounted() {
emitter.off('presentLoader', this.presentLoader);
emitter.off('dismissLoader', this.dismissLoader);
resetConfig()
},
setup() {
const router = useRouter();
Expand Down
13 changes: 0 additions & 13 deletions src/adapter/index.ts

This file was deleted.

113 changes: 113 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import axios from "axios";
import { setupCache } from "axios-cache-adapter"
import OfflineHelper from "@/offline-helper"
import emitter from "@/event-bus"
import store from "@/store";
import { StatusCodes } from "http-status-codes";
import router from "@/router"

axios.interceptors.request.use((config: any) => {
// TODO: pass csrf token
const token = store.getters["user/getUserToken"];
if (token) {
config.headers["api_key"] = token;
config.headers["Content-Type"] = "application/json";
}

return config;
});

// TODO: need to update this as per the changes in the Moqui response format, if required.
axios.interceptors.response.use(function (response: any) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data

// TODO: explore more on a secure way to store the csrf token
// Cannot store it in cookies or localStorage as its not safe
// https://stackoverflow.com/questions/67062876/is-it-secure-to-store-a-csrf-token-value-in-the-dom
// https://stackoverflow.com/questions/62289684/what-is-the-correct-way-for-a-client-to-store-a-csrf-token
const csrfToken = response.headers["x-csrf-token"]
const meta = document.createElement("meta")
meta.name = "csrf"
meta.content = csrfToken
document.getElementsByTagName("head")[0].appendChild(meta)

document.cookie = `x-csrf-token=${csrfToken}`

return response;
}, function (error: any) {
// TODO Handle it in a better way
// Currently when the app gets offline, the time between adding a loader and removing it is fractional due to which loader dismiss is called before loader present
// which cause loader to run indefinitely
// Following gives dismiss loader a delay of 100 microseconds to get both the actions in sync
setTimeout(() => emitter.emit("dismissLoader"), 100);
if (error.response) {
// TODO Handle case for failed queue request
const { status } = error.response;
if (status === StatusCodes.UNAUTHORIZED) {
store.dispatch("user/logout");
router.push("/login")
}
}
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});

const maxAge = process.env.VUE_APP_CACHE_MAX_AGE ? parseInt(process.env.VUE_APP_CACHE_MAX_AGE) : 0;
const axiosCache = setupCache({
maxAge: maxAge * 1000
})


/**
* Generic method to call APIs
*
* @param {string} url - API Url
* @param {string=} method - "GET", "PUT", "POST", "DELETE , and "PATCH"
* @param {any} [data] - Optional: `data` is the data to be sent as the request body. Only applicable for request methods "PUT", "POST", "DELETE , and "PATCH"
* When no `transformRequest` is set, must be of one of the following types:
* - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
* - Browser only: FormData, File, Blob
* - Node only: Stream, Buffer
* @param {any} [params] - Optional: `params` are the URL parameters to be sent with the request. Must be a plain object or a URLSearchParams object
* @param {boolean} [cache] - Optional: Apply caching to it
* @param {boolean} [queue] - Optional: Apply offline queueing to it
* @return {Promise} Response from API as returned by Axios
*/
const api = async (customConfig: any) => {
// Prepare configuration
const config: any = {
url: customConfig.url,
method: customConfig.method,
data: customConfig.data,
params: customConfig.params,
// withCredentials: true
}

const baseURL = store.getters["user/getInstanceUrl"];
if (baseURL) config.baseURL = `https://${baseURL}.hotwax.io/rest/s1/available-to-promise/`;
if(customConfig.cache) config.adapter = axiosCache.adapter;
const networkStatus = await OfflineHelper.getNetworkStatus();
if (customConfig.queue && !networkStatus.connected) {
if (!config.headers) config.headers = { ...axios.defaults.headers.common, ...config.headers };
emitter.emit("queueTask", {
callbackEvent: customConfig.callbackEvent,
payload: config
});
} else {
return axios(config);
}
}

/**
* Client method to directly pass configuration to axios
*
* @param {any} config - API configuration
* @return {Promise} Response from API as returned by Axios
*/
const client = (config: any) => {
return axios.request(config);
}

export { api as default, client, axios };
4 changes: 0 additions & 4 deletions src/authorization/Actions.ts

This file was deleted.

8 changes: 0 additions & 8 deletions src/authorization/Rules.ts

This file was deleted.

124 changes: 0 additions & 124 deletions src/authorization/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/CreateGroupModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<script setup lang="ts">
import { IonButton, IonButtons, IonContent, IonFab, IonFabButton, IonHeader, IonIcon, IonInput, IonItem, IonList, IonTextarea, IonTitle, IonToolbar, modalController } from "@ionic/vue";
import { closeOutline, checkmarkDone } from "ionicons/icons";
import { translate } from '@hotwax/dxp-components'
import { translate } from "@/i18n";

function closeModal() {
modalController.dismiss();
Expand Down
20 changes: 0 additions & 20 deletions src/components/ErrorMessageModal.vue

This file was deleted.

Loading