diff --git a/src/models/LocalStorage.ts b/src/models/LocalStorage.ts index 68611c1..0edab6b 100644 --- a/src/models/LocalStorage.ts +++ b/src/models/LocalStorage.ts @@ -4,6 +4,7 @@ export enum LocalStorageItem { TokenScopes = 'token-scopes', MarketingId = 'marketing-id', SuperappAuth = 'superapp-auth', + AppToken = 'app-token', } export class LocalStorage { diff --git a/src/models/index.ts b/src/models/index.ts index 88c6ebb..de2f537 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -90,6 +90,13 @@ export type Lecturer = timetableComponents['schemas']['LecturerGet']; export type Room = timetableComponents['schemas']['RoomGet']; export type StudyGroup = timetableComponents['schemas']['GroupGet']; +// app models +export interface AppToken { + appId: number; + token: string | undefined; + expire: number; +} + // general models export interface Entity { id: number; diff --git a/src/store/apps.ts b/src/store/apps.ts index c6c8657..51a3605 100644 --- a/src/store/apps.ts +++ b/src/store/apps.ts @@ -1,9 +1,48 @@ import { defineStore } from 'pinia'; import { ref } from 'vue'; -import { Category } from '@/models'; +import { Category, AppToken } from '@/models'; +import { LocalStorage, LocalStorageItem } from '@/models/LocalStorage'; export const useAppsStore = defineStore('apps', () => { const categories = ref([]); + const appTokens = ref([]); - return { categories }; + const addAppToken = (appId: number, token: string | undefined, expire: number) => { + appTokens.value.push({ + appId, + token, + expire, + }); + LocalStorage.set(LocalStorageItem.AppToken, appTokens.value); + }; + + const checkAppToken = (appId: number) => { + const appToken = appTokens.value.find(item => item.appId === appId); + const currentDate = new Date(); + + if (!appToken) { + return undefined; + } else { + if (appToken.expire < currentDate.getTime()) { + appTokens.value.splice(appTokens.value.indexOf(appToken), 1); + LocalStorage.set(LocalStorageItem.AppToken, appTokens.value); + return undefined; + } else { + return appToken.token; + } + } + }; + + const getTokensFromStorage = () => { + appTokens.value = LocalStorage.getObject(LocalStorageItem.AppToken) ?? []; + }; + + return { + categories, + appTokens, + + addAppToken, + checkAppToken, + getTokensFromStorage, + }; }); diff --git a/src/views/apps/ApplicationFrame.vue b/src/views/apps/ApplicationFrame.vue index a99a19a..2bd3996 100644 --- a/src/views/apps/ApplicationFrame.vue +++ b/src/views/apps/ApplicationFrame.vue @@ -8,11 +8,14 @@ import { AuthApi } from '@/api/controllers/auth/AuthApi'; import { ServiceData } from '@/models'; import apiClient from '@/api/'; import { msInHour } from '@/utils/time'; +import { useAppsStore } from '@/store/apps'; const route = useRoute(); const toolbar = useToolbar(); const router = useRouter(); const profileStore = useProfileStore(); +const appStore = useAppsStore(); +appStore.getTokensFromStorage(); enum AppState { WaitLoad = 1, @@ -47,14 +50,15 @@ const composeUrl = async (url: URL, token: string | null, scopes: string[]) => { return url; }; -function showApproveScopesScreen() { - appState.value = AppState.WaitApprove; - // immediately return a Promise - // return new Promise(resolve => { - // watch(userScopeApproved, value => resolve(value)); - // }); - return true; -} +// function showApproveScopesScreen() { +// appState.value = AppState.WaitApprove; +// // immediately return a Promise +// // Раскомментить, если появятся сторонние приложения +// // return new Promise(resolve => { +// // watch(userScopeApproved, value => resolve(value)); +// // }); +// return true; +// } const getToken = async () => { // Запрашиваем токен. Для этого: @@ -67,14 +71,10 @@ const getToken = async () => { return; } - console.log(scopes.value); const valuesToSearch = new Set(scopes.value); - console.log(valuesToSearch); userInfo.user_scopes.forEach(item => { - console.log(item); if (valuesToSearch.has(item.name)) { - console.log(' found'); // TODO: Поменять name на comment, когда допилю ручку me (и, возможно, поменять /me на /scope) if (item.name !== undefined && item.name !== null) { scopeNamesToRequest.value.push(item.name); @@ -85,27 +85,33 @@ const getToken = async () => { // 2. Если нужен токен без скоупов, то пропускаем запрос на разрешение у пользователя if (scopes.value.length != 0) { // 2.1 Показываем пользователю список прав, которые приложение запрашивает, и кнопки "разрешить"/"запретить" - const scopesApproved = await showApproveScopesScreen(); + const scopesApproved = true; // await showApproveScopesScreen(); // 2.2 Если пользователь не разрешает – возваращаем undefined if (!scopesApproved) return undefined; } // 3. Если пользователь разрешает – запрашиваем токен на Auth api и возвращаем его - const expiresDate = new Date(Date.now() + msInHour); - const { data } = await apiClient.POST('/auth/session', { - body: { - scopes: scopes.value.length == 0 ? [] : scopes.value, - expires: expiresDate.toISOString(), - }, - }); - if (!data) { - appState.value = AppState.Error; - return; - } - profileStore.id = data.user_id; + const storageToken = appStore.checkAppToken(appId); + if (storageToken) { + return storageToken; + } else { + const expiresDate = new Date(Date.now() + msInHour / 60); + const { data } = await apiClient.POST('/auth/session', { + body: { + scopes: scopes.value.length == 0 ? [] : scopes.value, + expires: expiresDate.toISOString(), + }, + }); + if (!data) { + appState.value = AppState.Error; + return; + } + profileStore.id = data.user_id; + appStore.addAppToken(appId, data.token ?? undefined, expiresDate.getTime()); - return data.token; + return data.token; + } }; const openApp = async (data: ServiceData) => { @@ -114,6 +120,7 @@ const openApp = async (data: ServiceData) => { appState.value = AppState.Error; return; } + // Приложения открываем только по https if (data.link === undefined || !data.link?.startsWith('https://')) { appState.value = AppState.Error; diff --git a/src/views/auth/AuthView.vue b/src/views/auth/AuthView.vue index 969d3c9..4999343 100644 --- a/src/views/auth/AuthView.vue +++ b/src/views/auth/AuthView.vue @@ -8,9 +8,6 @@ import ForgotPassordForm from '@/components/ForgotPassordForm.vue'; import IrdomAuthButton from '@/components/IrdomAuthButton.vue'; import { authButtons } from '@/constants/authButtons'; import { ref } from 'vue'; -import { useProfileStore } from '@/store/profile'; - -const profileStore = useProfileStore(); const toolbar = useToolbar(); @@ -41,7 +38,6 @@ const switchState = async (to: Step) => { } }; -console.log(profileStore.token); toolbar.setup({ title: 'Вход в профиль' });