Skip to content

Commit 25b9782

Browse files
authored
Merge pull request #118 from buggregator/feature/auth
Added OAuth0 authentication support on the backend.
2 parents 6c71c24 + 727647e commit 25b9782

File tree

15 files changed

+247
-29
lines changed

15 files changed

+247
-29
lines changed

layouts/blank.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<template>
2+
<div class="main-layout">
3+
<slot />
4+
</div>
5+
</template>
6+
7+
<script lang="ts">
8+
</script>
9+
10+
<style lang="scss" scoped>
11+
@import "assets/mixins";
12+
13+
.main-layout {
14+
@apply flex min-h-screen items-stretch relative;
15+
}
16+
</style>

layouts/default.vue

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class="main-layout__sidebar"
55
:api-version="apiVersion"
66
:client-version="clientVersion"
7+
:profile="profile"
78
/>
89

910
<div class="main-layout__content">
@@ -34,10 +35,11 @@ export default defineComponent({
3435
const { themeType, isFixedHeader } = storeToRefs(settingsStore);
3536
3637
const {
37-
api: { getVersion },
38+
api: { getVersion, getProfile },
3839
} = useSettings();
3940
4041
const apiVersion = await getVersion();
42+
const profile = await getProfile();
4143
4244
const { events } = useEvents();
4345
@@ -55,6 +57,7 @@ export default defineComponent({
5557
? `v${apiVersion}`
5658
: `@${apiVersion}`,
5759
clientVersion,
60+
profile,
5861
};
5962
},
6063
});
@@ -69,10 +72,7 @@ export default defineComponent({
6972
7073
.main-layout__sidebar {
7174
@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;
72-
}
73-
74-
.main-layout__header {
75-
@apply flex-none w-full h-10;
75+
@include layout-sidebar;
7676
}
7777
7878
.main-layout__content {
@@ -82,8 +82,4 @@ export default defineComponent({
8282
@apply flex flex-col h-full flex-1;
8383
}
8484
}
85-
86-
.main-layout__sidebar {
87-
@include layout-sidebar;
88-
}
8985
</style>

middleware/auth.global.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { useNuxtApp, navigateTo } from "#app"
2+
3+
export default defineNuxtRouteMiddleware(async (to, from) => {
4+
const app = useNuxtApp()
5+
const {localStorage} = window;
6+
7+
if (!app.$appSettings.auth.enabled) {
8+
return;
9+
}
10+
11+
// todo: move token to a store
12+
if (to.name !== 'login' && !app.$authToken.token) {
13+
return navigateTo('/login');
14+
}
15+
16+
if (to.name === 'login' && to?.query?.token) {
17+
localStorage?.setItem('token', to.query.token);
18+
// todo: use store
19+
app.$authToken.token = to.query.token;
20+
return navigateTo('/');
21+
}
22+
})

nuxt.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export default defineNuxtConfig({
2626
],
2727
},
2828
},
29+
plugins: [
30+
'~/plugins/auth',
31+
],
2932
dir: {
3033
static: 'static',
3134
},

pages/login.vue

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<script setup lang="ts">
2+
import { useNuxtApp, navigateTo } from "#app"
3+
import { REST_API_URL } from "~/src/shared/lib/io";
4+
import { IconSvg } from "~/src/shared/ui";
5+
6+
definePageMeta({
7+
layout: 'blank'
8+
})
9+
10+
const app = useNuxtApp()
11+
12+
const redirect = async () => {
13+
await navigateTo(`${REST_API_URL}/${app.$appSettings.auth.login_url}`, {
14+
external: true
15+
})
16+
}
17+
</script>
18+
19+
<template>
20+
<div class="login-page">
21+
<div class="login-form-container">
22+
<IconSvg class="login-form--logo" name="logo"/>
23+
<div class="login-form">
24+
<div class="login-form-left-block">
25+
<h1 class="login-form--title">Welcome Back</h1>
26+
<p class="pb-2 text-center text-sm text-gray-800">Let's get you signed in.</p>
27+
<button class="login-form--button" @click="redirect">
28+
<IconSvg class="w-6" name="lock" fill="currentcolor"/>
29+
Continue to SSO
30+
</button>
31+
</div>
32+
<div class="login-form-right-block"
33+
style="background: url('/bg.jpg'); background-size: cover; background-position: center center;">
34+
</div>
35+
</div>
36+
</div>
37+
</div>
38+
</template>
39+
40+
41+
<style lang="scss" scoped>
42+
@import "assets/mixins";
43+
44+
.login-page {
45+
@apply bg-gray-800;
46+
@apply h-screen w-screen;
47+
}
48+
49+
.login-form-container {
50+
@apply flex flex-col items-center justify-center flex-1;
51+
@apply px-4 sm:px-0;
52+
@apply h-full;
53+
}
54+
55+
.login-form {
56+
@apply flex w-full sm:w-3/4 md:w-2/3 xl:w-1/2 h-96;
57+
@apply shadow-2xl;
58+
@apply bg-gray-200;
59+
@apply rounded-2xl;
60+
@apply mt-10;
61+
}
62+
63+
.login-form-left-block {
64+
@apply flex flex-col flex-1 justify-center items-center;
65+
@apply mb-8 p-12;
66+
@apply w-1/2;
67+
}
68+
69+
.login-form-right-block {
70+
@apply w-0 md:w-1/2 h-full rounded-r-2xl;
71+
}
72+
73+
.login-form--logo {
74+
@apply w-48 text-gray-200;
75+
}
76+
77+
.login-form--title {
78+
@apply text-4xl text-center font-thin;
79+
@apply my-10;
80+
}
81+
82+
.login-form--button {
83+
@apply bg-blue-500 hover:bg-blue-600 transition-colors;
84+
@apply text-white rounded;
85+
@apply px-4 py-2 gap-2;
86+
@apply inline-flex;
87+
}
88+
</style>

plugins/auth.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useSettings } from "~/src/shared/lib/use-settings";
2+
3+
const {localStorage} = window;
4+
5+
// todo: use store for token
6+
export default defineNuxtPlugin(async () => {
7+
const {
8+
api: {getSettings},
9+
} = useSettings();
10+
11+
let settings = {
12+
auth: {
13+
enabled: false,
14+
login_url: '/login',
15+
},
16+
version: '0.0.0',
17+
}
18+
19+
try {
20+
settings = await getSettings()
21+
} catch (e) {
22+
console.error('Server is not available!')
23+
}
24+
25+
if (!settings.auth.enabled) {
26+
return {
27+
provide: {
28+
authToken: {token: null},
29+
appSettings: settings
30+
}
31+
}
32+
}
33+
34+
const token: string | null = localStorage?.getItem('token')
35+
36+
return {
37+
provide: {
38+
authToken: {token},
39+
appSettings: settings
40+
}
41+
}
42+
})

src/entities/sentry/ui/sentry-exception/sentry-exception-frame.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const toggleOpen = () => {
124124
}
125125
126126
.sentry-exception-frame__head-title-dd {
127-
@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;
127+
@apply w-5 h-4 flex justify-center shadow py-1 rounded transform rotate-180;
128128
}
129129
130130
.sentry-exception-frame__head-title-dd--visible {

src/entities/sentry/ui/sentry-exception/sentry-exception.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ const exceptionFrames = computed(() => {
1616
const frames = props.exception.stacktrace.frames || [];
1717
1818
if (props.maxFrames > 0) {
19-
return frames.reverse().slice(0, props.maxFrames);
19+
return frames.slice(0, props.maxFrames);
2020
}
2121
22-
return frames;
22+
return [...frames].reverse();
2323
});
2424
</script>
2525

src/shared/lib/io/centrifuge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ class WSConnection {
4242
return WSConnection.instance;
4343
}
4444

45-
public getCentrifuge () {
45+
public getCentrifuge() {
4646
return this.centrifuge;
4747
}
4848
}
4949

5050

51-
export const useCentrifuge: TUseCentrifuge = () => ({ centrifuge: WSConnection.getInstance().getCentrifuge() })
51+
export const useCentrifuge: TUseCentrifuge = () => ({centrifuge: WSConnection.getInstance().getCentrifuge()})

src/shared/lib/io/use-events-requests.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { EventId, EventType , ServerEvent } from '../../types';
1+
import type { EventId, EventType, ServerEvent } from '../../types';
22
import { REST_API_URL } from "./constants";
3+
import { useNuxtApp } from "#app"
34

45
type TUseEventsRequests = () => {
56
getAll: () => Promise<ServerEvent<unknown>[]>,
@@ -11,23 +12,33 @@ type TUseEventsRequests = () => {
1112
getEventRestUrl: (param: EventId | undefined) => string
1213
}
1314

15+
// TODO: add 403 response handling
16+
1417
export const useEventsRequests: TUseEventsRequests = () => {
15-
const getEventRestUrl = (param?: string) => `${REST_API_URL}/api/event${param ? `/${param}` : 's'}`
18+
const app = useNuxtApp()
19+
const token: string | null = app.$authToken.token
20+
const headers = {"X-Auth-Token": token}
21+
const getEventRestUrl = (param?: string): string => `${REST_API_URL}/api/event${param ? `/${param}` : 's'}`
1622

17-
const getAll = () => fetch(getEventRestUrl())
23+
const getAll = () => fetch(getEventRestUrl(), {headers})
1824
.then((response) => response.json())
1925
.then((response) => {
2026
if (response?.data) {
2127
return response.data as ServerEvent<unknown>[]
2228
}
2329

30+
if (response?.code === 403) {
31+
console.error('Forbidden')
32+
return [];
33+
}
34+
2435
console.error('Fetch Error')
2536

2637
return [];
2738
})
2839
.then((events: ServerEvent<unknown>[]) => events)
2940

30-
const getSingle = (id: EventId) => fetch(getEventRestUrl(id))
41+
const getSingle = (id: EventId) => fetch(getEventRestUrl(id), {headers})
3142
.then((response) => response.json())
3243
.then((response) => {
3344
if (response?.data) {
@@ -36,22 +47,30 @@ export const useEventsRequests: TUseEventsRequests = () => {
3647
return null;
3748
})
3849

39-
const deleteSingle = (id: EventId) => fetch(getEventRestUrl(id), { method: 'DELETE' })
50+
const deleteSingle = (id: EventId) => fetch(getEventRestUrl(id), {method: 'DELETE', headers})
4051
.catch((err) => {
4152
console.error('Fetch Error', err)
4253
})
4354

44-
const deleteAll = () => fetch(getEventRestUrl(), { method: 'DELETE' })
55+
const deleteAll = () => fetch(getEventRestUrl(), {method: 'DELETE', headers})
4556
.catch((err) => {
4657
console.error('Fetch Error', err)
4758
})
4859

49-
const deleteList = (uuids: EventId[]) => fetch(getEventRestUrl(), { method: 'DELETE', body: JSON.stringify({ uuids }) })
60+
const deleteList = (uuids: EventId[]) => fetch(getEventRestUrl(), {
61+
method: 'DELETE',
62+
headers,
63+
body: JSON.stringify({uuids})
64+
})
5065
.catch((err) => {
5166
console.error('Fetch Error', err)
5267
})
5368

54-
const deleteByType = (type: EventType) => fetch(getEventRestUrl(), { method: 'DELETE', body: JSON.stringify({type}) })
69+
const deleteByType = (type: EventType) => fetch(getEventRestUrl(), {
70+
method: 'DELETE',
71+
headers,
72+
body: JSON.stringify({type})
73+
})
5574
.catch((err) => {
5675
console.error('Fetch Error', err)
5776
})

0 commit comments

Comments
 (0)