From 54b381cc088f90dad4d9ca2a6b9d07719e075200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Thu, 23 Apr 2026 03:27:48 +0300 Subject: [PATCH] frontend: add auth store, LoginPage, and AuthCallback --- frontend/src/pages/AuthCallback.vue | 38 ++++++++++++++++ frontend/src/pages/LoginPage.vue | 69 +++++++++++++++++++++++++++++ frontend/src/router/routes.js | 7 ++- frontend/src/services/github.js | 8 ++++ frontend/src/stores/auth.js | 61 +++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/AuthCallback.vue create mode 100644 frontend/src/pages/LoginPage.vue create mode 100644 frontend/src/services/github.js create mode 100644 frontend/src/stores/auth.js diff --git a/frontend/src/pages/AuthCallback.vue b/frontend/src/pages/AuthCallback.vue new file mode 100644 index 0000000..4daae8d --- /dev/null +++ b/frontend/src/pages/AuthCallback.vue @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/pages/LoginPage.vue b/frontend/src/pages/LoginPage.vue new file mode 100644 index 0000000..fd4ae6d --- /dev/null +++ b/frontend/src/pages/LoginPage.vue @@ -0,0 +1,69 @@ + + + diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js index 8050556..9b3b6db 100644 --- a/frontend/src/router/routes.js +++ b/frontend/src/router/routes.js @@ -2,7 +2,12 @@ const routes = [ { path: '/', component: () => import('layouts/MainLayout.vue'), - children: [{ path: '', component: () => import('pages/IndexPage.vue') }], + children: [ + { path: '', component: () => import('pages/IndexPage.vue') }, + { path: '/login', component: () => import('pages/LoginPage.vue') }, + { path: '/auth/github', component: () => import('pages/AuthCallback.vue') }, + { path: '/auth/gitlab', component: () => import('pages/AuthCallback.vue') }, + ], }, { path: '/:catchAll(.*)*', diff --git a/frontend/src/services/github.js b/frontend/src/services/github.js new file mode 100644 index 0000000..ae66329 --- /dev/null +++ b/frontend/src/services/github.js @@ -0,0 +1,8 @@ +const BASE = 'https://api.github.com' + +export async function fetchCurrentUser(token) { + const res = await fetch(`${BASE}/user`, { + headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' }, + }) + return res.json() +} diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js new file mode 100644 index 0000000..f7e234e --- /dev/null +++ b/frontend/src/stores/auth.js @@ -0,0 +1,61 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' +import { fetchCurrentUser } from '../services/github.js' + +const STORAGE_KEY = 'auth' + +export const useAuthStore = defineStore('auth', () => { + const provider = ref(null) + const token = ref(null) + const gitlabHost = ref(null) + const user = ref(null) + + const isLoggedIn = computed(() => !!token.value) + + // Hydrate from localStorage on store creation + const stored = localStorage.getItem(STORAGE_KEY) + if (stored) { + try { + const parsed = JSON.parse(stored) + provider.value = parsed.provider + token.value = parsed.token + gitlabHost.value = parsed.gitlabHost + user.value = parsed.user + } catch { + /* ignore */ + } + } + + async function loginWithToken(providerName, accessToken, host = 'gitlab.com') { + let fetchedUser = null + + if (providerName === 'github') { + fetchedUser = await fetchCurrentUser(accessToken) + } else if (providerName === 'gitlab') { + const res = await fetch(`https://${host}/api/v4/user`, { + headers: { Authorization: `Bearer ${accessToken}` }, + }) + fetchedUser = await res.json() + } + + provider.value = providerName + token.value = accessToken + gitlabHost.value = host + user.value = fetchedUser + + localStorage.setItem( + STORAGE_KEY, + JSON.stringify({ provider: providerName, token: accessToken, gitlabHost: host, user: fetchedUser }), + ) + } + + function logout() { + provider.value = null + token.value = null + gitlabHost.value = null + user.value = null + localStorage.removeItem(STORAGE_KEY) + } + + return { provider, token, gitlabHost, user, isLoggedIn, loginWithToken, logout } +})