diff --git a/layouts/blank.vue b/layouts/blank.vue
new file mode 100644
index 00000000..d01c2136
--- /dev/null
+++ b/layouts/blank.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/layouts/default.vue b/layouts/default.vue
index 38dce44f..eb726039 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -4,6 +4,7 @@
class="main-layout__sidebar"
:api-version="apiVersion"
:client-version="clientVersion"
+ :profile="profile"
/>
@@ -34,10 +35,11 @@ export default defineComponent({
const { themeType, isFixedHeader } = storeToRefs(settingsStore);
const {
- api: { getVersion },
+ api: { getVersion, getProfile },
} = useSettings();
const apiVersion = await getVersion();
+ const profile = await getProfile();
const { events } = useEvents();
@@ -55,6 +57,7 @@ export default defineComponent({
? `v${apiVersion}`
: `@${apiVersion}`,
clientVersion,
+ profile,
};
},
});
@@ -69,10 +72,7 @@ export default defineComponent({
.main-layout__sidebar {
@apply w-10 md:w-14 lg:w-16 flex-none border-r border-gray-200 dark:border-gray-700 z-50 w-full h-full sticky top-0 h-screen max-h-screen;
-}
-
-.main-layout__header {
- @apply flex-none w-full h-10;
+ @include layout-sidebar;
}
.main-layout__content {
@@ -82,8 +82,4 @@ export default defineComponent({
@apply flex flex-col h-full flex-1;
}
}
-
-.main-layout__sidebar {
- @include layout-sidebar;
-}
diff --git a/middleware/auth.global.ts b/middleware/auth.global.ts
new file mode 100644
index 00000000..e4301b42
--- /dev/null
+++ b/middleware/auth.global.ts
@@ -0,0 +1,22 @@
+import { useNuxtApp, navigateTo } from "#app"
+
+export default defineNuxtRouteMiddleware(async (to, from) => {
+ const app = useNuxtApp()
+ const {localStorage} = window;
+
+ if (!app.$appSettings.auth.enabled) {
+ return;
+ }
+
+ // todo: move token to a store
+ if (to.name !== 'login' && !app.$authToken.token) {
+ return navigateTo('/login');
+ }
+
+ if (to.name === 'login' && to?.query?.token) {
+ localStorage?.setItem('token', to.query.token);
+ // todo: use store
+ app.$authToken.token = to.query.token;
+ return navigateTo('/');
+ }
+})
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 7d140fde..ee3d550c 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -26,6 +26,9 @@ export default defineNuxtConfig({
],
},
},
+ plugins: [
+ '~/plugins/auth',
+ ],
dir: {
static: 'static',
},
diff --git a/pages/login.vue b/pages/login.vue
new file mode 100644
index 00000000..ebac9cd6
--- /dev/null
+++ b/pages/login.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
diff --git a/plugins/auth.ts b/plugins/auth.ts
new file mode 100644
index 00000000..32b38540
--- /dev/null
+++ b/plugins/auth.ts
@@ -0,0 +1,42 @@
+import { useSettings } from "~/src/shared/lib/use-settings";
+
+const {localStorage} = window;
+
+// todo: use store for token
+export default defineNuxtPlugin(async () => {
+ const {
+ api: {getSettings},
+ } = useSettings();
+
+ let settings = {
+ auth: {
+ enabled: false,
+ login_url: '/login',
+ },
+ version: '0.0.0',
+ }
+
+ try {
+ settings = await getSettings()
+ } catch (e) {
+ console.error('Server is not available!')
+ }
+
+ if (!settings.auth.enabled) {
+ return {
+ provide: {
+ authToken: {token: null},
+ appSettings: settings
+ }
+ }
+ }
+
+ const token: string | null = localStorage?.getItem('token')
+
+ return {
+ provide: {
+ authToken: {token},
+ appSettings: settings
+ }
+ }
+})
diff --git a/src/entities/sentry/ui/sentry-exception/sentry-exception-frame.vue b/src/entities/sentry/ui/sentry-exception/sentry-exception-frame.vue
index 7c1580e8..2e261aa6 100644
--- a/src/entities/sentry/ui/sentry-exception/sentry-exception-frame.vue
+++ b/src/entities/sentry/ui/sentry-exception/sentry-exception-frame.vue
@@ -124,7 +124,7 @@ const toggleOpen = () => {
}
.sentry-exception-frame__head-title-dd {
- @apply w-5 h-4 flex justify-center border border-purple-300 shadow bg-white dark:bg-gray-600 py-1 rounded transform rotate-180;
+ @apply w-5 h-4 flex justify-center shadow py-1 rounded transform rotate-180;
}
.sentry-exception-frame__head-title-dd--visible {
diff --git a/src/entities/sentry/ui/sentry-exception/sentry-exception.vue b/src/entities/sentry/ui/sentry-exception/sentry-exception.vue
index 1216c149..db7556eb 100644
--- a/src/entities/sentry/ui/sentry-exception/sentry-exception.vue
+++ b/src/entities/sentry/ui/sentry-exception/sentry-exception.vue
@@ -16,10 +16,10 @@ const exceptionFrames = computed(() => {
const frames = props.exception.stacktrace.frames || [];
if (props.maxFrames > 0) {
- return frames.reverse().slice(0, props.maxFrames);
+ return frames.slice(0, props.maxFrames);
}
- return frames;
+ return [...frames].reverse();
});
diff --git a/src/shared/lib/io/centrifuge.ts b/src/shared/lib/io/centrifuge.ts
index bd59c110..29e54eb0 100644
--- a/src/shared/lib/io/centrifuge.ts
+++ b/src/shared/lib/io/centrifuge.ts
@@ -42,10 +42,10 @@ class WSConnection {
return WSConnection.instance;
}
- public getCentrifuge () {
+ public getCentrifuge() {
return this.centrifuge;
}
}
-export const useCentrifuge: TUseCentrifuge = () => ({ centrifuge: WSConnection.getInstance().getCentrifuge() })
+export const useCentrifuge: TUseCentrifuge = () => ({centrifuge: WSConnection.getInstance().getCentrifuge()})
diff --git a/src/shared/lib/io/use-events-requests.ts b/src/shared/lib/io/use-events-requests.ts
index 4a967604..14fb8bdf 100644
--- a/src/shared/lib/io/use-events-requests.ts
+++ b/src/shared/lib/io/use-events-requests.ts
@@ -1,5 +1,6 @@
-import type { EventId, EventType , ServerEvent } from '../../types';
+import type { EventId, EventType, ServerEvent } from '../../types';
import { REST_API_URL } from "./constants";
+import { useNuxtApp } from "#app"
type TUseEventsRequests = () => {
getAll: () => Promise
[]>,
@@ -11,23 +12,33 @@ type TUseEventsRequests = () => {
getEventRestUrl: (param: EventId | undefined) => string
}
+// TODO: add 403 response handling
+
export const useEventsRequests: TUseEventsRequests = () => {
- const getEventRestUrl = (param?: string) => `${REST_API_URL}/api/event${param ? `/${param}` : 's'}`
+ const app = useNuxtApp()
+ const token: string | null = app.$authToken.token
+ const headers = {"X-Auth-Token": token}
+ const getEventRestUrl = (param?: string): string => `${REST_API_URL}/api/event${param ? `/${param}` : 's'}`
- const getAll = () => fetch(getEventRestUrl())
+ const getAll = () => fetch(getEventRestUrl(), {headers})
.then((response) => response.json())
.then((response) => {
if (response?.data) {
return response.data as ServerEvent[]
}
+ if (response?.code === 403) {
+ console.error('Forbidden')
+ return [];
+ }
+
console.error('Fetch Error')
return [];
})
.then((events: ServerEvent[]) => events)
- const getSingle = (id: EventId) => fetch(getEventRestUrl(id))
+ const getSingle = (id: EventId) => fetch(getEventRestUrl(id), {headers})
.then((response) => response.json())
.then((response) => {
if (response?.data) {
@@ -36,22 +47,30 @@ export const useEventsRequests: TUseEventsRequests = () => {
return null;
})
- const deleteSingle = (id: EventId) => fetch(getEventRestUrl(id), { method: 'DELETE' })
+ const deleteSingle = (id: EventId) => fetch(getEventRestUrl(id), {method: 'DELETE', headers})
.catch((err) => {
console.error('Fetch Error', err)
})
- const deleteAll = () => fetch(getEventRestUrl(), { method: 'DELETE' })
+ const deleteAll = () => fetch(getEventRestUrl(), {method: 'DELETE', headers})
.catch((err) => {
console.error('Fetch Error', err)
})
- const deleteList = (uuids: EventId[]) => fetch(getEventRestUrl(), { method: 'DELETE', body: JSON.stringify({ uuids }) })
+ const deleteList = (uuids: EventId[]) => fetch(getEventRestUrl(), {
+ method: 'DELETE',
+ headers,
+ body: JSON.stringify({uuids})
+ })
.catch((err) => {
console.error('Fetch Error', err)
})
- const deleteByType = (type: EventType) => fetch(getEventRestUrl(), { method: 'DELETE', body: JSON.stringify({type}) })
+ const deleteByType = (type: EventType) => fetch(getEventRestUrl(), {
+ method: 'DELETE',
+ headers,
+ body: JSON.stringify({type})
+ })
.catch((err) => {
console.error('Fetch Error', err)
})
diff --git a/src/shared/lib/use-api-transport/use-api-transport.ts b/src/shared/lib/use-api-transport/use-api-transport.ts
index 1deb1fee..4d4f07b9 100644
--- a/src/shared/lib/use-api-transport/use-api-transport.ts
+++ b/src/shared/lib/use-api-transport/use-api-transport.ts
@@ -8,6 +8,8 @@ const CHECK_CONNECTION_INTERVAL: number = 10000
let isEventsEmitted: boolean = false
export const useApiTransport = () => {
+ const nuxtApp = useNuxtApp()
+ const token = nuxtApp.$authToken.token
const {centrifuge} = useCentrifuge()
const eventsStore = useEventStore()
const connectionStore = useConnectionStore()
@@ -64,13 +66,12 @@ export const useApiTransport = () => {
checkWSConnectionFail(async () => {
const events = await getAll();
-
eventsStore.addList(events);
})
const deleteEvent = (eventId: EventId) => {
if (getWSConnection()) {
- return centrifuge.rpc(`delete:api/event/${eventId}`, undefined)
+ return centrifuge.rpc(`delete:api/event/${eventId}`, {token})
}
return deleteSingle(eventId);
@@ -78,7 +79,7 @@ export const useApiTransport = () => {
const deleteEventsAll = () => {
if (getWSConnection()) {
- return centrifuge.rpc(`delete:api/events`, undefined)
+ return centrifuge.rpc(`delete:api/events`, {token})
}
return deleteAll();
@@ -94,7 +95,7 @@ export const useApiTransport = () => {
}
if (getWSConnection()) {
- return centrifuge.rpc(`delete:api/events`, {uuids})
+ return centrifuge.rpc(`delete:api/events`, {uuids, token})
}
return deleteList(uuids);
@@ -102,7 +103,7 @@ export const useApiTransport = () => {
const deleteEventsByType = (type: EventType) => {
if (getWSConnection()) {
- return centrifuge.rpc(`delete:api/events`, {type})
+ return centrifuge.rpc(`delete:api/events`, {type, token})
}
return deleteByType(type);
@@ -111,13 +112,14 @@ export const useApiTransport = () => {
// NOTE: works only with ws
const rayStopExecution = (hash: RayContentLock["name"]) => {
centrifuge.rpc(`post:api/ray/locks/${hash}`, {
- stop_execution: true
+ stop_execution: true,
+ token
})
}
// NOTE: works only with ws
const rayContinueExecution = (hash: RayContentLock["name"]) => {
- centrifuge.rpc(`post:api/ray/locks/${hash}`, undefined)
+ centrifuge.rpc(`post:api/ray/locks/${hash}`, {token})
}
return {
diff --git a/src/shared/lib/use-settings/use-settings.ts b/src/shared/lib/use-settings/use-settings.ts
index 4a88af08..3dc74626 100644
--- a/src/shared/lib/use-settings/use-settings.ts
+++ b/src/shared/lib/use-settings/use-settings.ts
@@ -8,14 +8,29 @@ type TUseSettings = {
}
export const useSettings = (): TUseSettings => {
+ const nuxtApp = useNuxtApp()
+
+ // todo: we can get version from settings
const getAppVersion = () => fetch(`${REST_API_URL}/api/version`)
.then((response) => response.json())
.then((response) => response?.version || 'unknown')
.catch(() => 'unknown');
+ const getAppSettings = () => fetch(`${REST_API_URL}/api/settings`)
+ .then((response) => response.json())
+ .catch(() => 'unknown');
+
+ const getProfile = () => fetch(`${REST_API_URL}/api/me`, {
+ headers: {"X-Auth-Token": nuxtApp.$authToken.token}
+ })
+ .then((response) => response.json())
+ .catch(() => 'unknown');
+
return {
api: {
getVersion: getAppVersion,
+ getProfile,
+ getSettings: getAppSettings
}
}
}
diff --git a/src/shared/ui/icon-svg/icon-svg-originals/avatar.svg b/src/shared/ui/icon-svg/icon-svg-originals/avatar.svg
new file mode 100644
index 00000000..e69de29b
diff --git a/src/widgets/ui/layout-sidebar/layout-sidebar.vue b/src/widgets/ui/layout-sidebar/layout-sidebar.vue
index a966e852..544c3c2d 100644
--- a/src/widgets/ui/layout-sidebar/layout-sidebar.vue
+++ b/src/widgets/ui/layout-sidebar/layout-sidebar.vue
@@ -7,6 +7,7 @@ import { useConnectionStore } from "~/stores/connections";
type Props = {
apiVersion: string;
clientVersion: string;
+ profile: object;
};
defineProps();
@@ -64,6 +65,14 @@ const connectionText = computed(
+