diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index e6a38a14..ad4db87e 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -6,60 +6,38 @@ import SimulationRunView from '../views/SimulationRunView.vue' import ReportView from '../views/ReportView.vue' import InteractionView from '../views/InteractionView.vue' import LoginView from '../views/LoginView.vue' -import authState from '../store/auth' +import ForgotPasswordView from '../views/ForgotPasswordView.vue' +import ResetPasswordView from '../views/ResetPasswordView.vue' +import SetPasswordView from '../views/SetPasswordView.vue' +import AdminView from '../views/AdminView.vue' +import authState, { isAdmin } from '../store/auth' const routes = [ - { - path: '/login', - name: 'Login', - component: LoginView, - meta: { public: true } - }, - { - path: '/', - name: 'Home', - component: Home - }, - { - path: '/process/:projectId', - name: 'Process', - component: Process, - props: true - }, - { - path: '/simulation/:simulationId', - name: 'Simulation', - component: SimulationView, - props: true - }, - { - path: '/simulation/:simulationId/start', - name: 'SimulationRun', - component: SimulationRunView, - props: true - }, - { - path: '/report/:reportId', - name: 'Report', - component: ReportView, - props: true - }, - { - path: '/interaction/:reportId', - name: 'Interaction', - component: InteractionView, - props: true - } + // Públiques + { path: '/login', name: 'Login', component: LoginView, meta: { public: true } }, + { path: '/forgot-password', name: 'ForgotPassword', component: ForgotPasswordView, meta: { public: true } }, + { path: '/reset-password/:token', name: 'ResetPassword', component: ResetPasswordView, meta: { public: true }, props: true }, + { path: '/accept-invite/:token', name: 'AcceptInvite', component: SetPasswordView, meta: { public: true }, props: true }, + + // Privades + { path: '/', name: 'Home', component: Home }, + { path: '/process/:projectId', name: 'Process', component: Process, props: true }, + { path: '/simulation/:simulationId', name: 'Simulation', component: SimulationView, props: true }, + { path: '/simulation/:simulationId/start', name: 'SimulationRun', component: SimulationRunView, props: true }, + { path: '/report/:reportId', name: 'Report', component: ReportView, props: true }, + { path: '/interaction/:reportId', name: 'Interaction', component: InteractionView, props: true }, + + // Admin only + { path: '/admin', redirect: '/admin/users' }, + { path: '/admin/:tab', name: 'Admin', component: AdminView, props: true, meta: { requiresAdmin: true } }, ] -const router = createRouter({ - history: createWebHistory(), - routes -}) +const router = createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, from, next) => { if (to.meta?.public) return next() if (!authState.isAuthenticated) return next({ name: 'Login', query: { redirect: to.fullPath } }) + if (to.meta?.requiresAdmin && !isAdmin.value) return next({ name: 'Home' }) if (to.name === 'Login') return next({ name: 'Home' }) next() }) diff --git a/frontend/src/store/auth.js b/frontend/src/store/auth.js index dbb581dc..73132dc8 100644 --- a/frontend/src/store/auth.js +++ b/frontend/src/store/auth.js @@ -1,26 +1,41 @@ -import { reactive } from 'vue' +import { reactive, computed } from 'vue' const AUTH_KEY = 'mirofish_token' +const USER_KEY = 'mirofish_user' const state = reactive({ token: localStorage.getItem(AUTH_KEY) || null, - isAuthenticated: !!localStorage.getItem(AUTH_KEY) + user: JSON.parse(localStorage.getItem(USER_KEY) || 'null'), + get isAuthenticated() { return !!this.token } }) -export function setToken(token) { +export const isAdmin = computed(() => state.user?.role === 'admin') + +export function setAuth(token, user) { state.token = token - state.isAuthenticated = true + state.user = user localStorage.setItem(AUTH_KEY, token) + localStorage.setItem(USER_KEY, JSON.stringify(user)) } -export function clearToken() { +export function clearAuth() { state.token = null - state.isAuthenticated = false + state.user = null localStorage.removeItem(AUTH_KEY) + localStorage.removeItem(USER_KEY) } export function getToken() { return state.token } +// Compatibilitat enrere (LoginView usa setToken) +export function setToken(token) { + setAuth(token, state.user) +} + +export function clearToken() { + clearAuth() +} + export default state diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue new file mode 100644 index 00000000..d764dd3a --- /dev/null +++ b/frontend/src/views/AdminView.vue @@ -0,0 +1,9 @@ + + + diff --git a/frontend/src/views/ForgotPasswordView.vue b/frontend/src/views/ForgotPasswordView.vue new file mode 100644 index 00000000..96097895 --- /dev/null +++ b/frontend/src/views/ForgotPasswordView.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/frontend/src/views/ResetPasswordView.vue b/frontend/src/views/ResetPasswordView.vue new file mode 100644 index 00000000..9a616633 --- /dev/null +++ b/frontend/src/views/ResetPasswordView.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/frontend/src/views/SetPasswordView.vue b/frontend/src/views/SetPasswordView.vue new file mode 100644 index 00000000..5b0f9246 --- /dev/null +++ b/frontend/src/views/SetPasswordView.vue @@ -0,0 +1,100 @@ + + + + +