diff --git a/package-lock.json b/package-lock.json index bbbd796..1991c10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4932,6 +4932,11 @@ "is-symbol": "^1.0.2" } }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -7546,6 +7551,11 @@ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", "dev": true }, + "js-cookie": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.0.tgz", + "integrity": "sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s=" + }, "js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -11259,6 +11269,11 @@ "nanoid": "^2.0.0" } }, + "shvl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/shvl/-/shvl-1.3.1.tgz", + "integrity": "sha512-+rRPP46hloYUAEImJcqprUgXu+05Ikqr4h4V+w5i2zJy37nAqtkQKufs3+3S2fDq6JNRrHMIQhB/Vaex+jgAAw==" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -12962,11 +12977,35 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuejs-logger": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vuejs-logger/-/vuejs-logger-1.5.3.tgz", + "integrity": "sha512-jw+AQ+IMJBz18fA4opHsqaU7P7yQNugoGywT6i3DCd1BWqg9eUx03Fr21kayqGcP4dxUwhVkkjuOyeirxLJC8g==", + "requires": { + "es6-object-assign": "1.1.0" + } + }, "vuex": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.0.tgz", "integrity": "sha512-mdHeHT/7u4BncpUZMlxNaIdcN/HIt1GsGG5LKByArvYG/v6DvHcOxvDCts+7SRdCoIRGllK8IMZvQtQXLppDYg==" }, + "vuex-persistedstate": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/vuex-persistedstate/-/vuex-persistedstate-2.5.4.tgz", + "integrity": "sha512-XYJhKIwO+ZVlTaXyxKxnplrJ88Fnvk5aDw753bxzRw5/yMKLQ6lq9CDCBex2fwZaQcLibhtgJOxGCHjy9GLSlQ==", + "requires": { + "deepmerge": "^2.1.0", + "shvl": "^1.3.0" + }, + "dependencies": { + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + } + } + }, "vuex-typex": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/vuex-typex/-/vuex-typex-3.1.4.tgz", diff --git a/package.json b/package.json index 6039e56..4a26e88 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@vue/cli": "^3.4.1", "axios": "^0.18.0", "bootstrap-vue": "^2.0.0-rc.13", + "js-cookie": "^2.2.0", "vue": "^2.6.6", "vue-axios": "^2.1.4", "vue-class-component": "^6.0.0", @@ -20,7 +21,9 @@ "vue-i18n": "^8.9.0", "vue-property-decorator": "^7.0.0", "vue-router": "^3.0.1", + "vuejs-logger": "1.5.3", "vuex": "^3.0.1", + "vuex-persistedstate": "^2.5.4", "vuex-typex": "^3.1.4" }, "devDependencies": { diff --git a/src/$log.d.ts b/src/$log.d.ts new file mode 100644 index 0000000..087ab0f --- /dev/null +++ b/src/$log.d.ts @@ -0,0 +1,18 @@ +import Vue from 'vue'; +/* +Type augmentation for log plugin for typescript +https://vuejs.org/v2/guide/typescript.html#Augmenting-Types-for-Use-with-Plugins +https://github.com/justinkames/vuejs-logger/issues/24 +Tobias +*/ +declare module 'vue/types/vue' { + export interface VueConstructor { + $log: { + debug(...args: any[]): void; + info(...args: any[]): void; + warn(...args: any[]): void; + error(...args: any[]): void; + fatal(...args: any[]): void; + }; + } +} diff --git a/src/App.vue b/src/App.vue index aa61b6a..983b936 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,7 +3,8 @@ diff --git a/src/api/index.ts b/src/api/index.ts index 70ce73d..6b36d61 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,46 +1,51 @@ +import Vue from 'vue'; import axios from 'axios'; const API_URL = 'http://localhost:5443/api'; -export function fetchSurveys () { - return axios.get(`${API_URL}/surveys/`) +export function fetchSurveys() { + return axios.get(`${API_URL}/surveys/`); } -export function fetchSurvey (surveyId: string) { - return axios.get(`${API_URL}/surveys/${surveyId}/`) +export function fetchSurvey(surveyId: string) { + return axios.get(`${API_URL}/surveys/${surveyId}/`); } -export function saveSurveyResponse (surveyResponse: any) { - return axios.put(`${API_URL}/surveys/${surveyResponse.id}/`, surveyResponse) +export function saveSurveyResponse(surveyResponse: any) { + return axios.put(`${API_URL}/surveys/${surveyResponse.id}/`, surveyResponse); } -export function postNewSurvey (survey: any, jwt: any) { - return axios.post(`${API_URL}/surveys/`, survey, { headers: { Authorization: `Bearer ${jwt}` } }) +export function postNewSurvey(survey: any, jwt: any) { + return axios.post(`${API_URL}/surveys/`, survey, { headers: { Authorization: `Bearer ${jwt}` } }); } -export function authenticate (userData: any) { - return axios.post(`${API_URL}/auth/login`, userData) +export function authenticate(userData: any) { + return axios.post(`${API_URL}/auth/login`, userData); } -export function register (userData: any) { - return axios.post(`${API_URL}/auth/register`, userData) +export function register(userData: any) { + return axios.post(`${API_URL}/auth/register`, userData); } // This function probably isn't really useful here, as a user must be redirected to the actual OIDC provider. // -> So login involves user interaction through the frontend (and not just API calls). -export function oidc_login (redirection_url: any) { - return axios.get(`${API_URL}/auth/oidc`, redirection_url) +export function oidc_login(redirectionUrl: any) { + return axios.get(`${API_URL}/auth/oidc`, redirectionUrl); } -export function getFreshToken (refresh_token: any) { - return axios.get(`${API_URL}/auth/fresh`, refresh_token) +export function getFreshToken(refreshToken: any) { + return axios.get(`${API_URL}/auth/fresh`, refreshToken); } -export function getProviders () { - return axios.get(`${API_URL}/auth/providers`) +export function getProviders() { + return axios.get(`${API_URL}/auth/providers`); } -export function fetchUsers (jwt: any) { - console.log("JWT: " + jwt); - return axios.get(`${API_URL}/v1/user`, { headers: { Authorization: `Bearer ${jwt}` } }) +export function fetchUsers(jwt: any) { + return axios.get(`${API_URL}/v1/user`, { headers: { Authorization: `Bearer ${jwt}` } }); +} + +export function fetchProfile(jwt: any) { + Vue.$log.debug("JWT: "+ jwt); + return axios.get(`${API_URL}/v1/user/profile`, { headers: { Authorization: `Bearer ${jwt}` } }); } diff --git a/src/components/Profile.vue b/src/components/Profile.vue new file mode 100644 index 0000000..80c468c --- /dev/null +++ b/src/components/Profile.vue @@ -0,0 +1,58 @@ + + + + + + diff --git a/src/main.ts b/src/main.ts index 9a9e1bc..edee401 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,16 +4,31 @@ import VueAxios from 'vue-axios'; import App from './App.vue'; import router from './router'; import store from './store'; -import VueCookies from 'vue-cookies' +import VueCookies from 'vue-cookies'; +import VueLogger from 'vuejs-logger'; import i18n from '@/plugins/i18n'; // @ts-ignore import FlagIcon from 'vue-flag-icon'; // following is to avoid missing type definitions // const FlagIcon = require('vue-flag-icon'); +const isProduction = process.env.NODE_ENV === 'production'; + +const options = { + isEnabled: true, + // logLevel : isProduction ? 'error' : 'debug', + logLevel : 'debug', + stringifyArguments : false, + showLogLevel : true, + showMethodName : true, + separator: '|', + showConsoleColors: true +}; + +Vue.use(VueLogger, options); Vue.use(VueAxios, axios); Vue.use(FlagIcon); -Vue.use(VueCookies) +Vue.use(VueCookies); // setup fake backend // import { configureFakeBackend } from './helpers'; diff --git a/src/router.ts b/src/router.ts index a730e57..975e218 100644 --- a/src/router.ts +++ b/src/router.ts @@ -6,6 +6,7 @@ import NotFound from './views/NotFound.vue'; import Survey from '@/components/Survey.vue'; import NewSurvey from '@/components/NewSurvey.vue'; import Login from '@/components/Login.vue'; +import Profile from '@/components/Profile.vue'; import store from '@/store'; Vue.use(Router); @@ -22,6 +23,7 @@ export const router = new Router({ }, { path: '/login', + name: 'Login', component: Login, }, { @@ -47,9 +49,9 @@ export const router = new Router({ } }, }, { - path: '/login', - name: 'Login', - component: Login, + path: '/profile', + name: 'Profile', + component: Profile, }, { path: '*', diff --git a/src/store.ts b/src/store.ts index 4058419..138394f 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,6 @@ import Vue from 'vue'; import Vuex from 'vuex'; - +import createPersistedState from 'vuex-persistedstate' // imports of AJAX functions will go here import { @@ -11,7 +11,7 @@ import { postNewSurvey, authenticate, register, - oidc_login, fetchUsers, getFreshToken + oidc_login, fetchUsers, getFreshToken, fetchProfile, } from '@/api'; import { isValidJwt, EventBus } from '@/utils'; @@ -22,7 +22,7 @@ const state = { surveys: [], loginProviders: [], currentSurvey: {}, - user: {}, + profile: {}, users: [], access_token: '', refresh_token: '', @@ -51,51 +51,64 @@ const actions = { loadUsers(context: any) { return fetchUsers(context.state.access_token) .then((response) => { - console.log(response); - console.log(response.data); + Vue.$log.debug(response); + Vue.$log.debug(response.data); context.commit('setUsers', { users: response.data }); EventBus.$emit('usersLoaded', response.data); }) .catch((error) => { - console.log('Error loading users!', error); + Vue.$log.warn('Error loading users!', error); EventBus.$emit('failedLoadingUsers', error); }); }, + loadProfile(context: any) { + return fetchProfile(context.state.access_token) + .then((response) => { + Vue.$log.debug(response); + Vue.$log.debug(response.data); + context.commit('setProfile', { profile: response.data }); + EventBus.$emit('profileLoaded', response.data); + }) + .catch((error) => { + Vue.$log.warn('Error loading profile!', error); + EventBus.$emit('failedLoadingProfile', error); + }); + }, loadLoginProviders(context: any) { return getProviders() .then((response) => { context.commit('setLoginProviderData', {providers: response.data}); EventBus.$emit('loginProvidersLoaded', response.data); - }) + }); }, login(context: any, userData: any) { context.commit('setUserData', { userData }); return authenticate(userData) .then((response) => context.commit('setJwtToken', { tokens: response.data })) .catch((error) => { - console.log('Error Authenticating: ', error); + Vue.$log.warn('Error Authenticating: ', error); EventBus.$emit('failedAuthentication', error); }); }, - oidc_login(context: any, redirection_url: any) { - //context.commit('setUserData', { userData }); - return oidc_login(redirection_url) + oidc_login(context: any, redirectionUrl: any) { + // context.commit('setUserData', { userData }); + return oidc_login(redirectionUrl) .then((response) => context.commit('setJwtToken', { tokens: response.data })) .catch((error) => { - console.log('Error Authenticating: ', error); + Vue.$log.warn('Error Authenticating: ', error); EventBus.$emit('failedAuthentication', error); }); }, refreshToken(context: any) { - //context.commit('setUserData', { userData }); + // context.commit('setUserData', { userData }); return getFreshToken(context.state.refresh_token) .then((response) => context.commit('setTokens', { tokens: response.data })) .catch((error) => { - console.log('Error Refreshing token: ', error); + Vue.$log.warn('Error Refreshing token: ', error); EventBus.$emit('failedRefreshingToken', error); }); }, - storeTokens(context: any, tokens: any){ + storeTokens(context: any, tokens: any) { context.commit('setTokens', {tokens}); EventBus.$emit('storedTokens'); }, @@ -104,7 +117,7 @@ const actions = { return register(userData) .then(context.dispatch('login', userData)) .catch((error) => { - console.log('Error Registering: ', error); + Vue.$log.warn('Error Registering: ', error); EventBus.$emit('failedRegistering: ', error); }); }, @@ -138,33 +151,38 @@ const mutations = { } } }, - setLoginProviderData(sState: any, payload: any){ - console.log("got loginProviders = ", payload); + setLoginProviderData(sState: any, payload: any) { + Vue.$log.debug('got loginProviders = ', payload); sState.loginProviders = payload.providers; }, + // probably old ... setUserData(sState: any, payload: any) { - console.log('setUserData payload = ', payload); + Vue.$log.debug('setUserData payload = ', payload); sState.userData = payload.userData; }, + setProfile(sState: any, payload: any) { + Vue.$log.debug('setProfile payload = ', payload); + sState.profile = payload.profile; + }, setJwtToken(sState: any, payload: any) { - console.log('setJwtToken payload = ', payload); + Vue.$log.debug('setJwtToken payload = ', payload); localStorage.tokens = payload.tokens; sState.jwt = payload.tokens.access_token; sState.jwt_refresh_token = payload.tokens.refresh_token; }, - setTokens(sState: any, payload:any){ - console.log('setTokens payload = ', payload); + setTokens(sState: any, payload: any) { + Vue.$log.debug('setTokens payload = ', payload); sState.access_token = payload.tokens.access_token; sState.refresh_token = payload.tokens.refresh_token; - console.log(sState.access_token); - console.log(sState.access_token); - } + Vue.$log.debug(sState.access_token); + Vue.$log.debug(sState.access_token); + }, }; const getters = { // reusable data accessors isAuthenticated(sState: any) { - return isValidJwt(sState.jwt.token); + return isValidJwt(sState.access_token); }, getLoginProviders(sState: any) { return sState.loginProviders; @@ -176,6 +194,7 @@ const store = new Vuex.Store({ actions, mutations, getters, + plugins: [createPersistedState()], }); export default store;