diff --git a/js/src/App.vue b/js/src/App.vue index 11f6b01d0..5c2513aa8 100644 --- a/js/src/App.vue +++ b/js/src/App.vue @@ -98,6 +98,7 @@ export default class App extends Vue { variables: { id: userId, email: userEmail, + isLoggedIn: true, }, }); } diff --git a/js/src/apollo/user.ts b/js/src/apollo/user.ts index 0aea70453..fdf1804bd 100644 --- a/js/src/apollo/user.ts +++ b/js/src/apollo/user.ts @@ -4,16 +4,18 @@ export const currentUser = { __typename: 'CurrentUser', id: null, email: null, + isLoggedIn: false, }, }, resolvers: { Mutation: { - updateCurrentUser: (_, { id, email }, { cache }) => { + updateCurrentUser: (_, { id, email, isLoggedIn }, { cache }) => { const data = { currentUser: { id, email, + isLoggedIn, __typename: 'CurrentUser', }, }; diff --git a/js/src/components/NavBar.vue b/js/src/components/NavBar.vue index 855ea2eea..967995037 100644 --- a/js/src/components/NavBar.vue +++ b/js/src/components/NavBar.vue @@ -18,17 +18,17 @@ @@ -45,7 +47,7 @@ diff --git a/js/src/graphql/user.ts b/js/src/graphql/user.ts index 07a16aba0..06c300404 100644 --- a/js/src/graphql/user.ts +++ b/js/src/graphql/user.ts @@ -28,13 +28,14 @@ export const CURRENT_USER_CLIENT = gql` query { currentUser @client { id, - email + email, + isLoggedIn, } } `; export const UPDATE_CURRENT_USER_CLIENT = gql` -mutation UpdateCurrentUser($id: Int!, $email: String!) { - updateCurrentUser(id: $id, email: $email) @client +mutation UpdateCurrentUser($id: Int!, $email: String!, $isLoggedIn: Boolean!) { + updateCurrentUser(id: $id, email: $email, isLoggedIn: $isLoggedIn) @client } `; diff --git a/js/src/router/actor.ts b/js/src/router/actor.ts index 56fc2d1d6..e984d2bc1 100644 --- a/js/src/router/actor.ts +++ b/js/src/router/actor.ts @@ -3,6 +3,7 @@ import CreateGroup from '@/views/Group/Create.vue'; import Group from '@/views/Group/Group.vue'; import GroupList from '@/views/Group/GroupList.vue'; import Identities from '@/views/Account/Identities.vue'; +import { RouteConfig } from 'vue-router'; export enum ActorRouteName { IDENTITIES = 'Identities', @@ -12,7 +13,7 @@ export enum ActorRouteName { PROFILE = 'Profile', } -export const actorRoutes = [ +export const actorRoutes: RouteConfig[] = [ { path: '/identities', name: ActorRouteName.IDENTITIES, diff --git a/js/src/router/error.ts b/js/src/router/error.ts new file mode 100644 index 000000000..c811c010e --- /dev/null +++ b/js/src/router/error.ts @@ -0,0 +1,16 @@ +import { beforeRegisterGuard } from '@/router/guards/register-guard'; +import { RouteConfig } from 'vue-router'; +import ErrorPage from '@/views/Error.vue'; + +export enum ErrorRouteName { + ERROR = 'Error', +} + +export const errorRoutes: RouteConfig[] = [ + { + path: '/error', + name: ErrorRouteName.ERROR, + component: ErrorPage, + beforeEnter: beforeRegisterGuard, + }, +]; diff --git a/js/src/router/event.ts b/js/src/router/event.ts index 18562c378..b466e1e7f 100644 --- a/js/src/router/event.ts +++ b/js/src/router/event.ts @@ -2,6 +2,7 @@ import EventList from '@/views/Event/EventList.vue'; import Location from '@/views/Location.vue'; import CreateEvent from '@/views/Event/Create.vue'; import Event from '@/views/Event/Event.vue'; +import { RouteConfig } from 'vue-router'; export enum EventRouteName { EVENT_LIST = 'EventList', @@ -11,7 +12,7 @@ export enum EventRouteName { LOCATION = 'Location', } -export const eventRoutes = [ +export const eventRoutes: RouteConfig[] = [ { path: '/events/list/:location?', name: EventRouteName.EVENT_LIST, diff --git a/js/src/router/guards/auth-guard.ts b/js/src/router/guards/auth-guard.ts new file mode 100644 index 000000000..fb1c0c9ec --- /dev/null +++ b/js/src/router/guards/auth-guard.ts @@ -0,0 +1,21 @@ +import { NavigationGuard } from 'vue-router'; +import { UserRouteName } from '@/router/user'; +import { LoginErrorCode } from '@/types/login-error-code.model'; +import { AUTH_TOKEN } from '@/constants'; + +export const authGuardIfNeeded: NavigationGuard = async function (to, from, next) { + if (to.meta.requiredAuth !== true) return next(); + + // We can't use "currentUser" from apollo here because we may not have loaded the user from the local storage yet + if (!localStorage.getItem(AUTH_TOKEN)) { + return next({ + name: UserRouteName.LOGIN, + query: { + code: LoginErrorCode.NEED_TO_LOGIN, + redirect: to.fullPath, + }, + }); + } + + return next(); +}; diff --git a/js/src/router/guards/register-guard.ts b/js/src/router/guards/register-guard.ts new file mode 100644 index 000000000..eb4dbc0c9 --- /dev/null +++ b/js/src/router/guards/register-guard.ts @@ -0,0 +1,23 @@ +import { apolloProvider } from '@/vue-apollo'; +import { CONFIG } from '@/graphql/config'; +import { IConfig } from '@/types/config.model'; +import { NavigationGuard } from 'vue-router'; +import { ErrorRouteName } from '@/router/error'; +import { ErrorCode } from '@/types/error-code.model'; + +export const beforeRegisterGuard: NavigationGuard = async function (to, from, next) { + const { data } = await apolloProvider.defaultClient.query({ + query: CONFIG, + }); + + const config: IConfig = data.config; + + if (config.registrationsOpen === false) { + return next({ + name: ErrorRouteName.ERROR, + query: { code: ErrorCode.REGISTRATION_CLOSED }, + }); + } + + return next(); +}; diff --git a/js/src/router/index.ts b/js/src/router/index.ts index 0663b53d4..a322e8b9a 100644 --- a/js/src/router/index.ts +++ b/js/src/router/index.ts @@ -5,6 +5,8 @@ import Home from '@/views/Home.vue'; import { UserRouteName, userRoutes } from './user'; import { EventRouteName, eventRoutes } from '@/router/event'; import { ActorRouteName, actorRoutes } from '@/router/actor'; +import { ErrorRouteName, errorRoutes } from '@/router/error'; +import { authGuardIfNeeded } from '@/router/guards/auth-guard'; Vue.use(Router); @@ -20,6 +22,7 @@ export const RouteName = { ...UserRouteName, ...EventRouteName, ...ActorRouteName, + ...ErrorRouteName, }; const router = new Router({ @@ -29,6 +32,7 @@ const router = new Router({ ...userRoutes, ...eventRoutes, ...actorRoutes, + ...errorRoutes, { path: '/', @@ -46,4 +50,6 @@ const router = new Router({ ], }); +router.beforeEach(authGuardIfNeeded); + export default router; diff --git a/js/src/router/user.ts b/js/src/router/user.ts index 3b7a8f329..6d652a341 100644 --- a/js/src/router/user.ts +++ b/js/src/router/user.ts @@ -5,6 +5,8 @@ import Validate from '@/views/User/Validate.vue'; import ResendConfirmation from '@/views/User/ResendConfirmation.vue'; import SendPasswordReset from '@/views/User/SendPasswordReset.vue'; import PasswordReset from '@/views/User/PasswordReset.vue'; +import { beforeRegisterGuard } from '@/router/guards/register-guard'; +import { RouteConfig } from 'vue-router'; export enum UserRouteName { REGISTER = 'Register', @@ -16,13 +18,14 @@ export enum UserRouteName { LOGIN = 'Login', } -export const userRoutes = [ +export const userRoutes: RouteConfig[] = [ { path: '/register/user', name: UserRouteName.REGISTER, component: RegisterUser, props: true, meta: { requiredAuth: false }, + beforeEnter: beforeRegisterGuard, }, { path: '/register/profile', diff --git a/js/src/types/current-user.model.ts b/js/src/types/current-user.model.ts index 94f32b1b1..0c1f6277b 100644 --- a/js/src/types/current-user.model.ts +++ b/js/src/types/current-user.model.ts @@ -1,4 +1,5 @@ export interface ICurrentUser { id: number; email: string; + isLoggedIn: boolean; } diff --git a/js/src/types/error-code.model.ts b/js/src/types/error-code.model.ts new file mode 100644 index 000000000..ba33435f3 --- /dev/null +++ b/js/src/types/error-code.model.ts @@ -0,0 +1,4 @@ +export enum ErrorCode { + UNKNOWN = 'unknown', + REGISTRATION_CLOSED = 'registration_closed', +} diff --git a/js/src/types/login-error-code.model.ts b/js/src/types/login-error-code.model.ts new file mode 100644 index 000000000..781c1e2ec --- /dev/null +++ b/js/src/types/login-error-code.model.ts @@ -0,0 +1,3 @@ +export enum LoginErrorCode { + NEED_TO_LOGIN = 'rouge', +} diff --git a/js/src/views/Error.vue b/js/src/views/Error.vue new file mode 100644 index 000000000..0d5715b17 --- /dev/null +++ b/js/src/views/Error.vue @@ -0,0 +1,25 @@ + + + diff --git a/js/src/views/User/Login.vue b/js/src/views/User/Login.vue index 196ade974..5e587dec1 100644 --- a/js/src/views/User/Login.vue +++ b/js/src/views/User/Login.vue @@ -5,7 +5,12 @@ Welcome back! -
+ + + You need to login. + + +
{{ error }} @@ -49,6 +54,10 @@
+ + + You are already logged-in. + @@ -58,16 +67,21 @@ import { LOGIN } from '@/graphql/auth'; import { validateEmailField, validateRequiredField } from '@/utils/validators'; import { saveUserData } from '@/utils/auth'; import { ILogin } from '@/types/login.model'; -import { UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user'; +import { CURRENT_USER_CLIENT, UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user'; import { onLogin } from '@/vue-apollo'; import { RouteName } from '@/router'; -import { IConfig } from '@/types/config.model'; -import { CONFIG } from '@/graphql/config'; +import { LoginErrorCode } from '@/types/login-error-code.model' +import { ICurrentUser } from '@/types/current-user.model' +import { CONFIG } from '@/graphql/config' +import { IConfig } from '@/types/config.model' @Component({ apollo: { config: { query: CONFIG + }, + currentUser: { + query: CURRENT_USER_CLIENT } } }) @@ -75,33 +89,39 @@ export default class Login extends Vue { @Prop({ type: String, required: false, default: '' }) email!: string; @Prop({ type: String, required: false, default: '' }) password!: string; + LoginErrorCode = LoginErrorCode; + + errorCode: LoginErrorCode | null = null; config!: IConfig; + currentUser!: ICurrentUser; + credentials = { email: '', password: '', }; validationSent = false; + errors: string[] = []; rules = { required: validateRequiredField, email: validateEmailField, }; - user: any; - beforeCreate() { - if (this.user) { - this.$router.push('/'); - } - } + private redirect: string | null = null; mounted() { this.credentials.email = this.email; this.credentials.password = this.password; + + let query = this.$route.query; + this.errorCode = query[ 'code' ] as LoginErrorCode; + this.redirect = query[ 'redirect' ] as string; } async loginAction(e: Event) { e.preventDefault(); - this.errors.splice(0); + + this.errors = []; try { const result = await this.$apollo.mutate<{ login: ILogin }>({ @@ -119,12 +139,17 @@ export default class Login extends Vue { variables: { id: result.data.login.user.id, email: this.credentials.email, + isLoggedIn: true, }, }); onLogin(this.$apollo); - this.$router.push({ name: RouteName.HOME }); + if (this.redirect) { + this.$router.push(this.redirect) + } else { + this.$router.push({ name: RouteName.HOME }); + } } catch (err) { console.error(err); err.graphQLErrors.forEach(({ message }) => {