From 485c052f518d95c45d8a0a3dd482bf32127f065a Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 16:34:59 +0300 Subject: [PATCH 01/11] fix(server/auth): return self after logged in --- server/app/routes/auth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/app/routes/auth.py b/server/app/routes/auth.py index d4c82c7..e7ea716 100644 --- a/server/app/routes/auth.py +++ b/server/app/routes/auth.py @@ -33,7 +33,7 @@ def post(self): return 'Incorrect password', 400 else: login_user(User(user), remember=True) - return 'Logged in', 200 + return parse_json(get_user_by_id(current_user.get_id())), 200 @login_required def get(self): @@ -58,4 +58,4 @@ def get(self): }) admin = db.users.find_one(result.inserted_id) login_user(User(admin), remember=True) - return "Logged in as root" + return parse_json(get_user_by_id(current_user.get_id())) From b42e4afb9d1a35835fd5fecbd255d168d3beb0c1 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 16:35:12 +0300 Subject: [PATCH 02/11] feat: userApi --- client/src/components/routes/auth/api.ts | 20 ++++++++++++++++++++ client/src/components/routes/auth/types.ts | 9 +++++++++ 2 files changed, 29 insertions(+) create mode 100644 client/src/components/routes/auth/api.ts create mode 100644 client/src/components/routes/auth/types.ts diff --git a/client/src/components/routes/auth/api.ts b/client/src/components/routes/auth/api.ts new file mode 100644 index 0000000..f6a2642 --- /dev/null +++ b/client/src/components/routes/auth/api.ts @@ -0,0 +1,20 @@ +import { api } from "@/api"; +import { User } from "@/components/routes/auth/types"; + +export function fetchUser() { + return api.get("/auth/login"); +} + +export function login(login: string, password: string) { + return api.post("/auth/login", { login, password }); +} + +export function logout() { + return api.delete("/auth/login"); +} + +export function devLogin() { + return api.get("/auth/login/dev"); +} + +export const UserAPI = { fetchUser, login, logout, devLogin }; diff --git a/client/src/components/routes/auth/types.ts b/client/src/components/routes/auth/types.ts new file mode 100644 index 0000000..080e226 --- /dev/null +++ b/client/src/components/routes/auth/types.ts @@ -0,0 +1,9 @@ +export interface User { + _id: { + $oid: string; + }; + login: string; + password: string; + name: string; + role: string; +} From 2d8c56728bf0f74872fd58dff0500f503cd35ddc Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 16:35:16 +0300 Subject: [PATCH 03/11] feat: user store --- client/src/store/user.ts | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 client/src/store/user.ts diff --git a/client/src/store/user.ts b/client/src/store/user.ts new file mode 100644 index 0000000..70c561e --- /dev/null +++ b/client/src/store/user.ts @@ -0,0 +1,42 @@ +import { defineStore } from "pinia"; +import { computed, readonly, ref } from "vue"; +import { User } from "@/components/routes/auth/types"; +import { UserAPI } from "@/components/routes/auth/api"; + +export const useUserStore = defineStore("user", () => { + const user = ref(null); + + async function fetchUser() { + try { + user.value = (await UserAPI.fetchUser()).data; + } catch (err) { + user.value = null; + } + } + + async function login(login: string, password: string) { + user.value = (await UserAPI.login(login, password)).data; + } + + async function devLogin() { + user.value = (await UserAPI.devLogin()).data; + } + + async function logout() { + user.value = null; + await UserAPI.logout(); + } + + const isAuthed = computed(() => user.value !== null); + const role = computed(() => user.value?.role || null); + + return { + user: readonly(user), + fetchUser, + login, + logout, + devLogin, + isAuthed, + role, + }; +}); From 03c62a18996363cdad54687682f0373a4b09f3d3 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 16:35:45 +0300 Subject: [PATCH 04/11] feat: initial user fetching --- client/src/main.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/client/src/main.ts b/client/src/main.ts index 04eb87f..16caa8b 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -9,12 +9,20 @@ import { vBsTooltip } from "@/bootstrap/tooltip"; import { plugin, defaultConfig } from "@formkit/vue"; import "@formkit/themes/genesis"; import { createPinia } from "pinia"; +import { useUserStore } from "@/store/user"; -const pinia = createPinia(); +async function bootstrap() { + const pinia = createPinia(); -const app = createApp(App); -app.use(router); -app.use(plugin, defaultConfig()); -app.use(pinia); -app.directive("bs-tooltip", vBsTooltip); -app.mount("#app"); + const userStore = useUserStore(pinia); + await userStore.fetchUser(); + + const app = createApp(App); + app.use(router); + app.use(plugin, defaultConfig()); + app.use(pinia); + app.directive("bs-tooltip", vBsTooltip); + app.mount("#app"); +} + +bootstrap(); From 383db854f1cfc26d8a20a52e1d30fa1764e4b8fd Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 16:36:07 +0300 Subject: [PATCH 05/11] fix(api): credentials --- client/src/api/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/api/index.ts b/client/src/api/index.ts index 10a7c69..765bdf0 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -8,6 +8,7 @@ export const baseURL = `${serverURL}/api`; export const api: AxiosInstance = axios.create({ baseURL, + withCredentials: true, }); const min_map_zoom = import.meta.env.MIN_ZOOM ?? 1; From c26a2b937bfa01824062b5a3183e18cb65d4a66c Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 16:36:18 +0300 Subject: [PATCH 06/11] feat: auth page --- client/src/App.vue | 30 ++++++++++ .../components/routes/auth/AuthComponent.vue | 60 +++++++++++++++++++ client/src/router/index.ts | 8 +++ client/src/views/AuthView.vue | 7 +++ 4 files changed, 105 insertions(+) create mode 100644 client/src/components/routes/auth/AuthComponent.vue create mode 100644 client/src/views/AuthView.vue diff --git a/client/src/App.vue b/client/src/App.vue index 60a4371..f3d1c3b 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -34,6 +34,21 @@ +
+ + + + +
@@ -45,6 +60,12 @@ From 6158195151492bcf1b24c94c75134554329a5ead Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 17:07:07 +0300 Subject: [PATCH 07/11] refactor: user -> types --- client/src/components/routes/auth/api.ts | 10 +++++----- client/src/store/user.ts | 2 +- .../routes/auth/types.ts => types/users.ts} | 0 3 files changed, 6 insertions(+), 6 deletions(-) rename client/src/{components/routes/auth/types.ts => types/users.ts} (100%) diff --git a/client/src/components/routes/auth/api.ts b/client/src/components/routes/auth/api.ts index f6a2642..f276a0c 100644 --- a/client/src/components/routes/auth/api.ts +++ b/client/src/components/routes/auth/api.ts @@ -1,19 +1,19 @@ import { api } from "@/api"; -import { User } from "@/components/routes/auth/types"; +import { User } from "@/types/users"; -export function fetchUser() { +function fetchUser() { return api.get("/auth/login"); } -export function login(login: string, password: string) { +function login(login: string, password: string) { return api.post("/auth/login", { login, password }); } -export function logout() { +function logout() { return api.delete("/auth/login"); } -export function devLogin() { +function devLogin() { return api.get("/auth/login/dev"); } diff --git a/client/src/store/user.ts b/client/src/store/user.ts index 70c561e..9e16801 100644 --- a/client/src/store/user.ts +++ b/client/src/store/user.ts @@ -1,6 +1,6 @@ import { defineStore } from "pinia"; import { computed, readonly, ref } from "vue"; -import { User } from "@/components/routes/auth/types"; +import { User } from "@/types/users"; import { UserAPI } from "@/components/routes/auth/api"; export const useUserStore = defineStore("user", () => { diff --git a/client/src/components/routes/auth/types.ts b/client/src/types/users.ts similarity index 100% rename from client/src/components/routes/auth/types.ts rename to client/src/types/users.ts From cf7e1d327e98d27c1a78a99a02fddb19195561f9 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 17:07:15 +0300 Subject: [PATCH 08/11] feat: admin api --- client/src/components/routes/users/api.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 client/src/components/routes/users/api.ts diff --git a/client/src/components/routes/users/api.ts b/client/src/components/routes/users/api.ts new file mode 100644 index 0000000..e6eee88 --- /dev/null +++ b/client/src/components/routes/users/api.ts @@ -0,0 +1,21 @@ +import { api } from "@/api"; +import { User } from "@/types/users"; +import { UserCreation } from "@/components/routes/users/types"; + +function getUsers() { + return api.get("/users"); +} + +function createUser(user: UserCreation) { + return api.post("/users", user); +} + +function deleteUser(id: string) { + return api.delete(`/users/user/${id}`); +} + +function updateUser(id: string, user: UserCreation) { + return api.put(`/users/user/${id}`, user); +} + +export const UserAdminAPI = { getUsers, createUser, deleteUser, updateUser }; From 19a9c39d78106e14432d85e1735cb425ac0408c9 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 17:07:24 +0300 Subject: [PATCH 09/11] feat: users roles --- client/src/config/users.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 client/src/config/users.ts diff --git a/client/src/config/users.ts b/client/src/config/users.ts new file mode 100644 index 0000000..37eed43 --- /dev/null +++ b/client/src/config/users.ts @@ -0,0 +1,4 @@ +export enum UserRole { + user = "user", + admin = "admin", +} From e5b0bec3d342c9635bebb8c57c8dd98324d0588a Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 17:07:36 +0300 Subject: [PATCH 10/11] feat: admin users page --- client/src/App.vue | 15 ++- .../routes/users/UsersComponent.vue | 93 +++++++++++++++++++ client/src/components/routes/users/types.ts | 3 + client/src/router/index.ts | 8 ++ client/src/views/UsersView.vue | 7 ++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 client/src/components/routes/users/UsersComponent.vue create mode 100644 client/src/components/routes/users/types.ts create mode 100644 client/src/views/UsersView.vue diff --git a/client/src/App.vue b/client/src/App.vue index f3d1c3b..060e09c 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -32,6 +32,16 @@ О проекте + +
@@ -39,14 +49,14 @@ v-if="userStore.isAuthed" class="bi bi-box-arrow-left" @click="logout" - > + /> - +
@@ -63,6 +73,7 @@ import ToasterComponent from "@/components/common/ToasterComponent.vue"; import { useUserStore } from "@/store/user"; import { useToaster } from "@/store/toaster"; import { ToastTypes } from "@/config/toast"; +import { UserRole } from "@/config/users"; const userStore = useUserStore(), toaster = useToaster(); diff --git a/client/src/components/routes/users/UsersComponent.vue b/client/src/components/routes/users/UsersComponent.vue new file mode 100644 index 0000000..52d1ec0 --- /dev/null +++ b/client/src/components/routes/users/UsersComponent.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/client/src/components/routes/users/types.ts b/client/src/components/routes/users/types.ts new file mode 100644 index 0000000..2637ee0 --- /dev/null +++ b/client/src/components/routes/users/types.ts @@ -0,0 +1,3 @@ +import { User } from "@/types/users"; + +export type UserCreation = Omit; diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 92695be..b27ee84 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -9,6 +9,7 @@ export const routeNames = { Object: "Object", Home: "Home", Auth: "Auth", + Users: "Users", }; export const routePaths = { @@ -20,6 +21,7 @@ export const routePaths = { [routeNames.Object]: "/object/:id/:name/:objectIndex", [routeNames.Home]: "/home", [routeNames.Auth]: "/auth", + [routeNames.Auth]: "/Users", }; export const routes: RouteRecordRaw[] = [ @@ -73,6 +75,12 @@ export const routes: RouteRecordRaw[] = [ path: routePaths[routeNames.Auth], component: () => import("@/views/AuthView.vue"), }, + + { + name: routeNames.Users, + path: routePaths[routeNames.Users], + component: () => import("@/views/UsersView.vue"), + }, ]; export const router = createRouter({ diff --git a/client/src/views/UsersView.vue b/client/src/views/UsersView.vue new file mode 100644 index 0000000..3f29f38 --- /dev/null +++ b/client/src/views/UsersView.vue @@ -0,0 +1,7 @@ + + + + + From df41b72a2ac0c167cfd754c01d7da7e3f1d05c05 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 26 Nov 2023 17:16:42 +0300 Subject: [PATCH 11/11] feat: rights --- client/src/App.vue | 7 ++++--- .../components/routes/maps/MapsListComponent.vue | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/client/src/App.vue b/client/src/App.vue index 060e09c..d42eb93 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -74,17 +74,18 @@ import { useUserStore } from "@/store/user"; import { useToaster } from "@/store/toaster"; import { ToastTypes } from "@/config/toast"; import { UserRole } from "@/config/users"; +import { computed } from "vue"; const userStore = useUserStore(), toaster = useToaster(); -const routes = [ +const routes = computed(() => [ routeNames.Map, routeNames.MapsList, routeNames.Queue, routeNames.ObjectsList, - routeNames.Upload, -]; + ...(userStore.isAuthed ? [routeNames.Upload] : []), +]); const routesTranslation = { [routeNames.Map]: "Глобальная карта", [routeNames.MapsList]: "Карты", diff --git a/client/src/components/routes/maps/MapsListComponent.vue b/client/src/components/routes/maps/MapsListComponent.vue index 30140bf..7b472e2 100644 --- a/client/src/components/routes/maps/MapsListComponent.vue +++ b/client/src/components/routes/maps/MapsListComponent.vue @@ -48,9 +48,12 @@ import FlagRenderer from "@/components/renderers/FlagRenderer.vue"; import Modal from "@/components/common/Modal.vue"; import { useImages } from "@/api/websocket/images"; import { ref } from "vue"; +import { useUserStore } from "@/store/user"; const router = useRouter(); +const userStore = useUserStore(); + const columnDefs: ColDef[] = [ { headerName: "Id", field: "id", flex: 2, minWidth: 80 }, { headerName: "Имя", field: "name", flex: 3, minWidth: 180 }, @@ -91,15 +94,20 @@ const columnDefs: ColDef[] = [ button: "btn-secondary", hide: (data) => !(data.ready && data.sliced), onClicked: (action, data) => { - router.push({ name: routeNames.Map, params: { y: data.location.coordinates[0][0], - x: data.location.coordinates[0][1] } }) - } + router.push({ + name: routeNames.Map, + params: { + y: data.location.coordinates[0][0], + x: data.location.coordinates[0][1], + }, + }); + }, }, { tooltip: "Удалить карту", icon: "bi bi-trash", button: "btn-danger", - hide: (data) => !(data.ready && data.sliced), + hide: (data) => !(data.ready && data.sliced) || !userStore.isAuthed, onClicked: (action, data) => { delElement.value = data.id; modal.value?.open();