From 044578e53a40ae8afbd45ba99a5fd9af5f1f2227 Mon Sep 17 00:00:00 2001 From: Timofey Malinin Date: Tue, 14 Nov 2023 08:02:20 +0000 Subject: [PATCH] Dev to main --- .env | 3 ++ .gitlab-ci.yml | 10 ++++ Dockerfile | 3 ++ src/Map/Layers/Layers.jsx | 21 +++++--- src/SignOut.jsx | 17 +++--- src/api.js | 16 ++++-- .../ImportMode/PointsFileUploadModal.jsx | 12 ++--- .../PendingPointsFilters.jsx | 4 +- src/pages/Login.jsx | 16 ++++-- src/stores/auth.js | 53 ++++++++++++++++++- src/stores/signin.js | 26 +++++++-- vite.config.ts | 2 +- 12 files changed, 143 insertions(+), 40 deletions(-) diff --git a/.env b/.env index cf067ee..91539c8 100644 --- a/.env +++ b/.env @@ -1 +1,4 @@ VITE_API_URL=https://postnet.dev.selftech.ru +VITE_KEYCLOAK_CLIENT_ID=postnet +VITE_KEYCLOAK_CLIENT_SECRET=K2yHweEUispkVeWn03VMk843sW2Moic5 +VITE_KEYCLOAK_URL=https://kk.dev.selftech.ru/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1c18545..36e2a1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,8 +17,13 @@ build-docker-dev: docker build --build-arg YC_CONTAINER_REGISTRY=${YC_CONTAINER_REGISTRY} --build-arg VITE_API_URL="https://postnet.dev.selftech.ru" + --build-arg VITE_KEYCLOAK_CLIENT_ID="postnet" + --build-arg VITE_KEYCLOAK_CLIENT_SECRET=${VITE_KEYCLOAK_CLIENT_SECRET} + --build-arg VITE_KEYCLOAK_URL="https://kk.dev.selftech.ru/" -t ${DOCKER_IMAGE_TAG}-dev . - docker push ${DOCKER_IMAGE_TAG}-dev + environment: + name: dev build-docker-prod: stage: build @@ -29,8 +34,13 @@ build-docker-prod: docker build --build-arg YC_CONTAINER_REGISTRY=${YC_CONTAINER_REGISTRY} --build-arg VITE_API_URL="https://postnet.selftech.ru" + --build-arg VITE_KEYCLOAK_CLIENT_ID="" + --build-arg VITE_KEYCLOAK_CLIENT_SECRET=${VITE_KEYCLOAK_CLIENT_SECRET} + --build-arg VITE_KEYCLOAK_URL="" -t ${DOCKER_IMAGE_TAG}-prod . - docker push ${DOCKER_IMAGE_TAG}-prod + environment: + name: prod auto-deploy-dev-kuber: extends: .deploy_base_kuber diff --git a/Dockerfile b/Dockerfile index a18a09c..439f839 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ ARG YC_CONTAINER_REGISTRY FROM ${YC_CONTAINER_REGISTRY}/public/node:16 as builder ARG VITE_API_URL +ARG VITE_KEYCLOAK_CLIENT_ID +ARG VITE_KEYCLOAK_CLIENT_SECRET +ARG VITE_KEYCLOAK_URL WORKDIR /usr/src/postamates_frontend ENV NODE_OPTIONS=--max_old_space_size=4096 COPY package*.json ./ diff --git a/src/Map/Layers/Layers.jsx b/src/Map/Layers/Layers.jsx index 8f3677f..4063be8 100644 --- a/src/Map/Layers/Layers.jsx +++ b/src/Map/Layers/Layers.jsx @@ -1,13 +1,16 @@ -import { Points } from "./Points"; -import { Layer, Source } from "react-map-gl"; -import { aoLayer, rayonLayer } from "./layers-config"; -import { BASE_URL } from "../../api"; -import { PVZ } from "./PVZ"; -import { OtherPostamates } from "./OtherPostamates"; -import { SelectedRegion } from "./SelectedRegion"; -import { transliterate } from "../../utils.js"; +import {Points} from "./Points"; +import {Layer, Source} from "react-map-gl"; +import {aoLayer, rayonLayer} from "./layers-config"; +import {BASE_URL} from "../../api"; +import {PVZ} from "./PVZ"; +import {OtherPostamates} from "./OtherPostamates"; +import {SelectedRegion} from "./SelectedRegion"; +import {transliterate} from "../../utils.js"; +import {useUpdateLayerCounter} from "../../stores/useUpdateLayerCounter.js"; export const Layers = ({ postGroups, otherGroups }) => { + const { updateCounter } = useUpdateLayerCounter(); + return ( <> { @@ -58,6 +62,7 @@ export const Layers = ({ postGroups, otherGroups }) => { diff --git a/src/SignOut.jsx b/src/SignOut.jsx index f342779..2b8351c 100644 --- a/src/SignOut.jsx +++ b/src/SignOut.jsx @@ -5,15 +5,16 @@ import { setAuth } from "./stores/auth"; import { useQuery } from "@tanstack/react-query"; import { Title } from "./components/Title"; -export function SignOut() { - const logOut = async () => { - await api.post("accounts/logout/"); - - setAuth(false); - }; +export const logOut = () => { + localStorage.removeItem('access_token'); + localStorage.removeItem('refresh_token'); + localStorage.removeItem('expires_in'); + setAuth(false); +}; +export function SignOut() { const { data } = useQuery(["profile"], async () => { - const { data } = await api.get("/accounts/profile/"); + const { data } = await api.get("/api/me/"); return data; }); @@ -22,7 +23,7 @@ export function SignOut() { - + <Title text={data?.username} classNameText={"lowercase"} /> <Button type="primary" block onClick={logOut}> <span className="mr-1">Выйти</span> <ArrowRightOutlined /> diff --git a/src/api.js b/src/api.js index 41e1e3b..dc84799 100644 --- a/src/api.js +++ b/src/api.js @@ -15,9 +15,15 @@ export const api = axios.create({ import.meta.env.MODE === "development" ? "http://localhost:5173/" : BASE_URL, - withCredentials: true, - xsrfHeaderName: "X-CSRFToken", - xsrfCookieName: "csrftoken", +}); + +api.interceptors.request.use(function (config) { + const token = localStorage.getItem("access_token"); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + + return config; }); export const useDbTableName = () => { @@ -215,7 +221,7 @@ export const useGetPermissions = () => { return useQuery(["permissions"], async () => { const { data } = await api.get("/api/me/"); - if (data?.groups?.includes("Редактор")) { + if (data?.groups?.includes("postnet_editor")) { return "editor"; } @@ -332,7 +338,7 @@ export const useGetPendingPointsRange = (dbTable) => { const rangesArr = RANGE_FILTERS_KEYS.map((key) => { if ((/d[0-9]/.test(key))) return; return { - [key]: [Math.floor(data[key][0]), Math.min(Math.ceil(data[key][1]), 4000)] + [key]: [Math.floor(data[key][0]), Math.ceil(data[key][1])] } }).filter(item => !!item); const ranges = Object.assign({}, ...rangesArr); diff --git a/src/modules/ImportMode/PointsFileUploadModal.jsx b/src/modules/ImportMode/PointsFileUploadModal.jsx index e212ed4..7049533 100644 --- a/src/modules/ImportMode/PointsFileUploadModal.jsx +++ b/src/modules/ImportMode/PointsFileUploadModal.jsx @@ -26,9 +26,10 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => { const myInterval = setInterval(async () => { const response = await getImportStatus(); setImportStatus(response.task_status); - if (response.task_status === "Перерасчет ML завершен") { + if (response.task_status === "Перерасчет ML завершен" || !isOpened) { setReport(response.data); setIsImporting(false); + toggleUpdateCounter(); clearInterval(myInterval); } }, 2000); @@ -68,13 +69,6 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => { </Button> ] return [ - <Button - key="close-button" - type="default" - onClick={onClose} - > - Отмена - </Button>, <Button key="ok-button" type="primary" @@ -112,7 +106,7 @@ export const PointsFileUploadModal = ({onClose, isOpened}) => { <Modal open={isOpened} title="Импорт точек" - onCancel={onClose} + onCancel={() => {if (!isImporting) onClose()}} width={400} footer={getFooter()} > diff --git a/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx b/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx index d800891..4150c80 100644 --- a/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx +++ b/src/modules/Sidebar/PendingPointsFilters/PendingPointsFilters.jsx @@ -32,8 +32,8 @@ export const PendingPointsFilters = () => { setFilterWithKey(newRanges[key], key); return; } - const gtChanged = ranges[key][0] !== newRanges[key][0]; - const ltChanged = ranges[key][1] !== newRanges[key][1]; + const gtChanged = ranges[key] && newRanges[key] && ranges[key][0] !== newRanges[key][0]; + const ltChanged = ranges[key] && newRanges[key] && ranges[key][1] !== newRanges[key][1]; if (gtChanged || ltChanged) setFilterWithKey(newRanges[key], key); }); diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index dcb3e3e..633142c 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -3,15 +3,25 @@ import { Alert, Button, Form, Input, Space, Typography } from "antd"; import { LockOutlined, UserOutlined } from "@ant-design/icons"; import React from "react"; import { Navigate } from "react-router-dom"; -import { isAuthorized$ } from "../stores/auth"; +import { isAuthorized$, refreshTokenIntervalFunction, setAuthLocalStorage } from "../stores/auth"; import { signin, signinError$, signinLoading$ } from "../stores/signin"; function LoginForm() { const signinError = useStore(signinError$); const signinLoading = useStore(signinLoading$); - const onFinish = (values) => { - signin(values); + const onFinish = async (values) => { + const data = await signin(values); + setAuthLocalStorage(data); + + let interval; + + setTimeout(async () => { + await refreshTokenIntervalFunction(); + interval = setInterval(async () => { + await refreshTokenIntervalFunction(); + }, (data.expires_in - 5) * 1000) + }, 0); }; return ( diff --git a/src/stores/auth.js b/src/stores/auth.js index 2b85ce8..ac52517 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -1,5 +1,6 @@ import { action, atom } from "nanostores"; import { api } from "../api"; +import { logOut } from "../SignOut.jsx"; export const userInfoLoading$ = atom(true); @@ -9,13 +10,63 @@ export const setAuth = action(isAuthorized$, "setAuth", (store, newValue) => { store.set(newValue); }); +export const refreshToken = () => { + const url = import.meta.env.VITE_KEYCLOAK_URL || 'https://kk.dev.selftech.ru/'; + const clientId = import.meta.env.VITE_KEYCLOAK_CLIENT_ID || 'postnet'; + const clientSecret = import.meta.env.VITE_KEYCLOAK_CLIENT_SECRET || 'K2yHweEUispkVeWn03VMk843sW2Moic5'; + + return api.request({ + url: "/realms/SST/protocol/openid-connect/token", + baseURL: url, + method: "POST", + data: { + grant_type: "refresh_token", + client_id: clientId, + client_secret: clientSecret, + token_type: "bearer", + refresh_token: localStorage.getItem("refresh_token") + }, + headers: { + 'Content-type': 'application/x-www-form-urlencoded', + }, + }); +} + +export const setAuthLocalStorage = (data) => { + localStorage.setItem("access_token", data.access_token); + localStorage.setItem("refresh_token", data.refresh_token); + localStorage.setItem("expires_in", data.expires_in); +} + +export const refreshTokenIntervalFunction = async () => { + try { + const { data: refreshData } = await refreshToken(); + setAuthLocalStorage(refreshData); + } catch (error) { + clearInterval(interval); + logOut(); + throw error; + } +} + async function checkAuth() { try { - await api.get("/accounts/profile/"); + const { data } = await refreshToken(); + setAuthLocalStorage(data); + + let interval; + + setTimeout(async () => { + await refreshTokenIntervalFunction(); + interval = setInterval(async () => { + await refreshTokenIntervalFunction(); + }, (data.expires_in - 5) * 1000) + }, 0); setAuth(true); } catch (e) { console.log("Not authorized"); + clearInterval(interval); } finally { userInfoLoading$.set(false); } diff --git a/src/stores/signin.js b/src/stores/signin.js index b290f97..51f0d82 100644 --- a/src/stores/signin.js +++ b/src/stores/signin.js @@ -31,8 +31,30 @@ export async function signin(values) { signinLoading$.set(true); signinError$.set(""); + const url = import.meta.env.VITE_KEYCLOAK_URL || 'https://kk.dev.selftech.ru/'; + const clientId = import.meta.env.VITE_KEYCLOAK_CLIENT_ID || 'postnet'; + const clientSecret = import.meta.env.VITE_KEYCLOAK_CLIENT_SECRET || 'K2yHweEUispkVeWn03VMk843sW2Moic5'; + + const auth = () => { + return api.request({ + url: "/realms/SST/protocol/openid-connect/token", + baseURL: url, + method: "POST", + data: { + "grant_type": "password", + client_id: clientId, + client_secret: clientSecret, + username: values.login, + password: values.password, + }, + headers: { + 'Content-type': 'application/x-www-form-urlencoded', + }, + }); + } + try { - const { data } = await api.post("accounts/login/", values); + const { data } = await auth(); setAuth(true); return data; @@ -50,8 +72,6 @@ export async function signin(values) { } } -export const resetSignin = function () {}; - export const signupLoading$ = atom(false); export const signupError$ = atom(""); diff --git a/vite.config.ts b/vite.config.ts index 7a5e279..c6b1934 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,8 +11,8 @@ export default defineConfig(({ mode }) => { plugins: [svgr(), react()], server: { proxy: { - "/account": env.VITE_API_URL, "/api": env.VITE_API_URL, + "/realms": "https://kk.dev.selftech.ru/", }, }, css: {