Add error screen
This commit is contained in:
parent
800e545ff5
commit
3c1f07f3b0
7 changed files with 217 additions and 107 deletions
|
@ -18,17 +18,28 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
|
||||||
) => {
|
) => {
|
||||||
if (!isObject(request.query)) {
|
if (!isObject(request.query)) {
|
||||||
server.log.warn('Request query is not an object');
|
server.log.warn('Request query is not an object');
|
||||||
throw server.httpErrors.badRequest();
|
await reply.redirect(urlJoin(
|
||||||
|
websitePrefix,
|
||||||
|
`/prompt-error?code=invalid_request&description=${
|
||||||
|
encodeURIComponent('Request query is not an object')
|
||||||
|
}`,
|
||||||
|
));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
validateParam('client_id', request.query.client_id);
|
validateParam('client_id', request.query.client_id);
|
||||||
validateParam('redirect_uri', request.query.redirect_uri);
|
validateParam('redirect_uri', request.query.redirect_uri);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ParamError) {
|
if (error instanceof ParamError) {
|
||||||
throw server.httpErrors.badRequest(error.message);
|
await reply.redirect(urlJoin(
|
||||||
|
websitePrefix,
|
||||||
|
`/prompt-error?code=invalid_request&description=${encodeURIComponent(error.message)}`,
|
||||||
|
));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
server.log.error(error);
|
server.log.error(error);
|
||||||
throw server.httpErrors.internalServerError();
|
await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=internal'));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const application = await database.applicationRepo.findOne({
|
const application = await database.applicationRepo.findOne({
|
||||||
|
@ -36,8 +47,14 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
|
||||||
clientId: request.query.client_id,
|
clientId: request.query.client_id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (application === undefined) throw server.httpErrors.badRequest('Unknown application');
|
if (application === undefined) {
|
||||||
if (!application.redirectUris.includes(request.query.redirect_uri)) throw server.httpErrors.badRequest('Redirect URI not registered');
|
await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=unknown_application'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!application.redirectUris.includes(request.query.redirect_uri)) {
|
||||||
|
await reply.redirect(urlJoin(websitePrefix, '/prompt-error?code=unknown_redirect_uri'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
validateParam('response_type', request.query.response_type);
|
validateParam('response_type', request.query.response_type);
|
||||||
|
|
|
@ -23,5 +23,11 @@ module.exports = {
|
||||||
'max-len': ['off']
|
'max-len': ['off']
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: '**/*.vue',
|
||||||
|
rules: {
|
||||||
|
'class-methods-use-this': ['off']
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
35
website/src/compontents/dialog-app.vue
Normal file
35
website/src/compontents/dialog-app.vue
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<v-app class="dialog-app">
|
||||||
|
<div>
|
||||||
|
<div class="text-h4 text-center mt-8 mx-2">
|
||||||
|
<span class="primary--text">Wulkanowy</span> Bridge
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-main class="px-4">
|
||||||
|
<v-sheet max-width="500" class="mx-auto mt-16" color="transparent">
|
||||||
|
<slot />
|
||||||
|
</v-sheet>
|
||||||
|
</v-main>
|
||||||
|
</v-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
name: 'DialogApp',
|
||||||
|
})
|
||||||
|
export default class DialogApp extends Vue {
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.dialog-app {
|
||||||
|
background-color: #f7f7f7 !important;
|
||||||
|
|
||||||
|
.v-card__text, .v-card__title {
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,107 +1,96 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app class="authenticate-prompt-app">
|
<dialog-app class="authenticate-prompt-app">
|
||||||
<div>
|
<v-alert type="error" text v-if="!promptId">
|
||||||
<div class="text-h4 text-center mt-8">
|
Brak wymaganego parametru <code>prompt_id</code>
|
||||||
<span class="primary--text">Wulkanowy</span> Bridge
|
</v-alert>
|
||||||
</div>
|
<v-alert type="error" text v-else-if="promptInfoError">
|
||||||
|
Nie udało się wczytać danych
|
||||||
|
<template #append>
|
||||||
|
<v-btn text color="error" @click="loadPromptInfo">
|
||||||
|
Spróbuj ponownie
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-alert>
|
||||||
|
<div class="d-flex align-center justify-center mt-16 mb-16" v-else-if="!promptInfo">
|
||||||
|
<v-progress-circular indeterminate :size="96" color="primary" />
|
||||||
</div>
|
</div>
|
||||||
<v-main class="px-4">
|
<template v-else>
|
||||||
<v-sheet max-width="500" class="mx-auto mt-16" color="transparent">
|
<div class="pb-1 text--secondary">
|
||||||
<v-alert type="error" text v-if="!promptId">
|
Krok <span class="primary--text">{{ step }}/3</span>
|
||||||
Brak wymaganego parametru <code>prompt_id</code>
|
</div>
|
||||||
</v-alert>
|
<v-card outlined>
|
||||||
<v-alert type="error" text v-else-if="promptInfoError">
|
<div class="d-flex justify-center mn-16 avatar__wrapper">
|
||||||
Nie udało się wczytać danych
|
<v-badge
|
||||||
<template #append>
|
:color="promptInfo.application.verified ? 'green' : 'grey'"
|
||||||
<v-btn text color="error" @click="loadPromptInfo">
|
offset-x="64"
|
||||||
Spróbuj ponownie
|
offset-y="16"
|
||||||
</v-btn>
|
bottom
|
||||||
</template>
|
:content="promptInfo.application.verified ? 'Zweryfikowana' : 'Niezweryfikowana'"
|
||||||
</v-alert>
|
:value="step === 1"
|
||||||
<div class="d-flex align-center justify-center mt-16 mb-16" v-else-if="!promptInfo">
|
>
|
||||||
<v-progress-circular indeterminate :size="96" color="primary" />
|
<transition name="scale">
|
||||||
</div>
|
<v-sheet
|
||||||
<template v-else>
|
v-if="step === 1"
|
||||||
<div class="pb-1 text--secondary">
|
width="128"
|
||||||
Krok <span class="primary--text">{{ step }}/3</span>
|
height="128"
|
||||||
</div>
|
class="avatar-sheet mx-4 overflow-hidden"
|
||||||
<v-card outlined>
|
outlined
|
||||||
<div class="d-flex justify-center mn-16 avatar__wrapper">
|
|
||||||
<v-badge
|
|
||||||
:color="promptInfo.application.verified ? 'green' : 'grey'"
|
|
||||||
offset-x="64"
|
|
||||||
offset-y="16"
|
|
||||||
bottom
|
|
||||||
:content="promptInfo.application.verified ? 'Zweryfikowana' : 'Niezweryfikowana'"
|
|
||||||
:value="step === 1"
|
|
||||||
>
|
>
|
||||||
<transition name="scale">
|
<v-sheet
|
||||||
<v-sheet
|
class="fill-height d-flex align-center justify-center"
|
||||||
v-if="step === 1"
|
:color="promptInfo.application.iconColor"
|
||||||
width="128"
|
>
|
||||||
height="128"
|
<v-img
|
||||||
class="avatar-sheet mx-4 overflow-hidden"
|
:src="promptInfo.application.iconUrl"
|
||||||
outlined
|
width="80"
|
||||||
|
height="80"
|
||||||
|
aspect-ratio="1"
|
||||||
|
contain
|
||||||
>
|
>
|
||||||
<v-sheet
|
<template v-slot:placeholder>
|
||||||
class="fill-height d-flex align-center justify-center"
|
<div class="fill-height d-flex align-center justify-center">
|
||||||
:color="promptInfo.application.iconColor"
|
<v-icon :size="80">
|
||||||
>
|
mdi-help
|
||||||
<v-img
|
</v-icon>
|
||||||
:src="promptInfo.application.iconUrl"
|
</div>
|
||||||
width="80"
|
</template>
|
||||||
height="80"
|
</v-img>
|
||||||
aspect-ratio="1"
|
</v-sheet>
|
||||||
contain
|
</v-sheet>
|
||||||
>
|
</transition>
|
||||||
<template v-slot:placeholder>
|
</v-badge>
|
||||||
<div class="fill-height d-flex align-center justify-center">
|
</div>
|
||||||
<v-icon :size="80">
|
<v-window :value="step">
|
||||||
mdi-help
|
<v-window-item :value="1">
|
||||||
</v-icon>
|
<overview-window
|
||||||
</div>
|
:promptInfo="promptInfo"
|
||||||
</template>
|
@next="toLoginWindow"
|
||||||
</v-img>
|
/>
|
||||||
</v-sheet>
|
</v-window-item>
|
||||||
</v-sheet>
|
<v-window-item :value="2" eager>
|
||||||
</transition>
|
<login-window
|
||||||
</v-badge>
|
ref="loginWindow"
|
||||||
</div>
|
:prompt-info="promptInfo"
|
||||||
<v-window :value="step">
|
@login="login"
|
||||||
<v-window-item :value="1">
|
@back="loginBack"
|
||||||
<overview-window
|
/>
|
||||||
:promptInfo="promptInfo"
|
</v-window-item>
|
||||||
@next="toLoginWindow"
|
<v-window-item :value="3">
|
||||||
/>
|
<students-window
|
||||||
</v-window-item>
|
v-if="students !== null"
|
||||||
<v-window-item :value="2" eager>
|
:prompt-info="promptInfo"
|
||||||
<login-window
|
:students="students"
|
||||||
ref="loginWindow"
|
@back="toLoginWindow"
|
||||||
:prompt-info="promptInfo"
|
/>
|
||||||
@login="login"
|
</v-window-item>
|
||||||
@back="loginBack"
|
</v-window>
|
||||||
/>
|
</v-card>
|
||||||
</v-window-item>
|
</template>
|
||||||
<v-window-item :value="3">
|
</dialog-app>
|
||||||
<students-window
|
|
||||||
v-if="students !== null"
|
|
||||||
:prompt-info="promptInfo"
|
|
||||||
:students="students"
|
|
||||||
@back="toLoginWindow"
|
|
||||||
/>
|
|
||||||
</v-window-item>
|
|
||||||
</v-window>
|
|
||||||
</v-card>
|
|
||||||
</template>
|
|
||||||
</v-sheet>
|
|
||||||
</v-main>
|
|
||||||
</v-app>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.authenticate-prompt-app {
|
.authenticate-prompt-app {
|
||||||
background-color: #f7f7f7 !important;
|
|
||||||
|
|
||||||
.avatar-sheet {
|
.avatar-sheet {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
@ -122,10 +111,6 @@
|
||||||
.scale-enter, .scale-leave-to {
|
.scale-enter, .scale-leave-to {
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-card__text, .v-card__title {
|
|
||||||
word-break: normal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -136,10 +121,13 @@ import { PromptInfo, Student } from '@/types';
|
||||||
import LoginWindow from '@/compontents/authenticate-prompt-windows/login-window.vue';
|
import LoginWindow from '@/compontents/authenticate-prompt-windows/login-window.vue';
|
||||||
import StudentsWindow from '@/compontents/authenticate-prompt-windows/students-window.vue';
|
import StudentsWindow from '@/compontents/authenticate-prompt-windows/students-window.vue';
|
||||||
import { sdk } from '@/pages/authenticate-prompt/sdk';
|
import { sdk } from '@/pages/authenticate-prompt/sdk';
|
||||||
|
import DialogApp from '@/compontents/dialog-app.vue';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
name: 'AuthenticatePromptApp',
|
name: 'AuthenticatePromptApp',
|
||||||
components: { LoginWindow, OverviewWindow, StudentsWindow },
|
components: {
|
||||||
|
LoginWindow, OverviewWindow, StudentsWindow, DialogApp,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export default class AuthenticatePromptApp extends Vue {
|
export default class AuthenticatePromptApp extends Vue {
|
||||||
@Ref() readonly loginWindow!: LoginWindow
|
@Ref() readonly loginWindow!: LoginWindow
|
||||||
|
|
48
website/src/pages/prompt-error/app.vue
Normal file
48
website/src/pages/prompt-error/app.vue
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<dialog-app>
|
||||||
|
<v-card outlined>
|
||||||
|
<v-card-title>Podczas autoryzacji wystąpił błąd</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-alert text type="error">
|
||||||
|
{{ errorMessage }}<br>
|
||||||
|
<code
|
||||||
|
v-if="errorDescription !== null"
|
||||||
|
class="d-block mt-3 py-2 px-sm-3"
|
||||||
|
>
|
||||||
|
{{ errorDescription }}
|
||||||
|
</code>
|
||||||
|
</v-alert>
|
||||||
|
Skontaktuj się z twórcą aplikacji w celu rozwiązania problemu
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</dialog-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
import DialogApp from '@/compontents/dialog-app.vue';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
name: 'PromptErrorApp',
|
||||||
|
components: { DialogApp },
|
||||||
|
})
|
||||||
|
export default class PromptErrorApp extends Vue {
|
||||||
|
errorCode: string | null = null;
|
||||||
|
|
||||||
|
errorDescription: string | null = null;
|
||||||
|
|
||||||
|
get errorMessage() {
|
||||||
|
if (this.errorCode === 'invalid_request') return 'Niepoprawny format zapytania';
|
||||||
|
if (this.errorCode === 'internal') return 'Błąd serwera';
|
||||||
|
if (this.errorCode === 'unknown_application') return 'Nie znaleziono aplikacji';
|
||||||
|
if (this.errorCode === 'unknown_redirect_uri') return 'Niepoprawne URI przekierowania';
|
||||||
|
return 'Nieznany błąd';
|
||||||
|
}
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
this.errorCode = searchParams.get('code');
|
||||||
|
this.errorDescription = searchParams.get('description');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
10
website/src/pages/prompt-error/main.ts
Normal file
10
website/src/pages/prompt-error/main.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import vuetify from '@/plugins/vuetify';
|
||||||
|
import AuthenticatePromptApp from './app.vue';
|
||||||
|
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
vuetify,
|
||||||
|
render: (h) => h(AuthenticatePromptApp),
|
||||||
|
}).$mount('#app');
|
|
@ -7,7 +7,13 @@ module.exports = {
|
||||||
entry: 'src/pages/authenticate-prompt/main.ts',
|
entry: 'src/pages/authenticate-prompt/main.ts',
|
||||||
template: 'public/index.html',
|
template: 'public/index.html',
|
||||||
filename: 'authenticate-prompt.html',
|
filename: 'authenticate-prompt.html',
|
||||||
title: 'Authorize application | Wulkanowy Bridge',
|
title: 'Autoryzuj aplikację | Wulkanowy Bridge',
|
||||||
|
},
|
||||||
|
'prompt-error': {
|
||||||
|
entry: 'src/pages/prompt-error/main.ts',
|
||||||
|
template: 'public/index.html',
|
||||||
|
filename: 'prompt-error.html',
|
||||||
|
title: 'Błąd autoryzacji | Wulkanowy Bridge',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue