@fdm-monster/client-next 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/dist/assets/MaterialIcons-Regular-BjXOXp5c.eot +0 -0
- package/dist/assets/MaterialIcons-Regular-DEUTIz1o.ttf +0 -0
- package/dist/assets/MaterialIcons-Regular-DOtZ65Va.woff2 +0 -0
- package/dist/assets/MaterialIcons-Regular-FsbMSDLx.woff +0 -0
- package/dist/assets/index-CHzfWKPO.css +5 -0
- package/dist/assets/index-CnWtkGvw.js +90 -0
- package/dist/assets/index-CnWtkGvw.js.map +1 -0
- package/dist/favicon.ico +0 -0
- package/dist/img/DavidZwart.jpg +0 -0
- package/dist/img/OIG.JYDC2RaWdz7g9.jpg +0 -0
- package/dist/img/OIG.jpg +0 -0
- package/dist/img/icons/android-chrome-256x256.png +0 -0
- package/dist/img/icons/android-chrome-384x384.png +0 -0
- package/dist/img/icons/android-chrome-512x512.png +0 -0
- package/dist/img/icons/favicon.svg +1 -0
- package/dist/img/logo.png +0 -0
- package/dist/img/logo.svg +1 -0
- package/dist/img/manifest.webmanifest +33 -0
- package/dist/img/octoprint-tentacle.svg +144 -0
- package/dist/img/thumbail_unknown.jpg +0 -0
- package/dist/img/vbanner.jpg +0 -0
- package/dist/index.html +24 -0
- package/dist/robots.txt +2 -0
- package/package.json +1 -1
- package/.all-contributorsrc +0 -57
- package/.editorconfig +0 -5
- package/.eslintrc.js +0 -126
- package/.github/FUNDING.yml +0 -3
- package/.github/workflows/release-client.yml +0 -94
- package/.github/workflows/vue-publish.yml +0 -26
- package/.prettierignore +0 -15
- package/.whitesource +0 -12
- package/renovate.json +0 -30
- package/src/App.vue +0 -60
- package/src/AppLoader.vue +0 -383
- package/src/assets/adjectives.json +0 -1468
- package/src/assets/logo.svg +0 -6
- package/src/assets/nouns.json +0 -4309
- package/src/auto-imports.d.ts +0 -139
- package/src/backend/app.service.ts +0 -39
- package/src/backend/auth.service.ts +0 -56
- package/src/backend/base.service.ts +0 -57
- package/src/backend/batch.service.ts +0 -37
- package/src/backend/camera-stream.service.ts +0 -33
- package/src/backend/custom-gcode.service.ts +0 -11
- package/src/backend/dto/octoprint-settings.dto.ts +0 -168
- package/src/backend/first-time-setup.service.ts +0 -17
- package/src/backend/floor.service.ts +0 -84
- package/src/backend/index.ts +0 -4
- package/src/backend/print-completions.service.ts +0 -11
- package/src/backend/printer-file.service.ts +0 -91
- package/src/backend/printer-group.service.ts +0 -62
- package/src/backend/printer-job.service.ts +0 -20
- package/src/backend/printer-settings.service.ts +0 -28
- package/src/backend/printers.service.ts +0 -136
- package/src/backend/server-private.service.ts +0 -55
- package/src/backend/server.api.ts +0 -132
- package/src/backend/settings.service.ts +0 -85
- package/src/backend/user.service.ts +0 -51
- package/src/components/AboutHelp/AboutView.vue +0 -164
- package/src/components/CameraGrid/CameraGridView.vue +0 -111
- package/src/components/FirstTimeSetup/FirstTimeSetupView.vue +0 -354
- package/src/components/Generic/Actions/PrinterConnectionAction.vue +0 -56
- package/src/components/Generic/Actions/PrinterCreateAction.vue +0 -22
- package/src/components/Generic/Actions/PrinterDeleteAction.vue +0 -29
- package/src/components/Generic/Actions/PrinterQuickStopAction.vue +0 -35
- package/src/components/Generic/Actions/PrinterSettingsAction.vue +0 -35
- package/src/components/Generic/Actions/PrinterUrlAction.vue +0 -24
- package/src/components/Generic/Actions/RefreshFilesAction.vue +0 -50
- package/src/components/Generic/Actions/SyncPrinterNameAction.vue +0 -36
- package/src/components/Generic/Dialogs/AddOrUpdateCameraStreamDialog.vue +0 -131
- package/src/components/Generic/Dialogs/AddOrUpdateFloorDialog.vue +0 -141
- package/src/components/Generic/Dialogs/AddOrUpdatePrinterDialog.vue +0 -303
- package/src/components/Generic/Dialogs/BaseDialog.vue +0 -81
- package/src/components/Generic/Dialogs/BatchJsonCreateDialog.vue +0 -109
- package/src/components/Generic/Dialogs/BatchReprintDialog.vue +0 -190
- package/src/components/Generic/Dialogs/PrinterChecksPanel.vue +0 -37
- package/src/components/Generic/Dialogs/PrinterControlDialog.vue +0 -202
- package/src/components/Generic/Dialogs/PrinterMaintenanceDialog.vue +0 -130
- package/src/components/Generic/Dialogs/YamlImportExportDialog.vue +0 -186
- package/src/components/Generic/Dialogs/dialog.constants.ts +0 -19
- package/src/components/Generic/FileExplorerSideNav.vue +0 -734
- package/src/components/Generic/Loaders/GridLoader.vue +0 -68
- package/src/components/Generic/NavigationDrawer.vue +0 -69
- package/src/components/Generic/PrintJobsMenu.vue +0 -148
- package/src/components/Generic/Snackbars/AppErrorSnackbar.vue +0 -64
- package/src/components/Generic/Snackbars/AppInfoSnackbar.vue +0 -63
- package/src/components/Generic/Snackbars/AppProgressSnackbar.vue +0 -158
- package/src/components/Generic/Vuetify/TooltipButton.vue +0 -47
- package/src/components/HelpOverlay/HelpOverlay.vue +0 -57
- package/src/components/Login/LoginForm.vue +0 -206
- package/src/components/Login/LoginView.spec.ts +0 -64
- package/src/components/Login/LoginView.vue +0 -65
- package/src/components/Login/Logo.vue +0 -13
- package/src/components/Login/PermissionDenied.vue +0 -109
- package/src/components/Login/RegistrationForm.vue +0 -207
- package/src/components/Login/RegistrationView.vue +0 -17
- package/src/components/Login/__snapshots__/LoginView.spec.ts.snap +0 -1051
- package/src/components/NotFound/NotFoundView.vue +0 -39
- package/src/components/PrintStatistics/PrintStatistics.vue +0 -168
- package/src/components/PrintStatistics/PrintStatisticsView.vue +0 -15
- package/src/components/PrinterGrid/HomeToolbar.vue +0 -90
- package/src/components/PrinterGrid/PrinterGrid.vue +0 -164
- package/src/components/PrinterGrid/PrinterGridTile.vue +0 -438
- package/src/components/PrinterGrid/PrinterGridView.vue +0 -210
- package/src/components/PrinterList/FileControlList.vue +0 -40
- package/src/components/PrinterList/PrinterDetails.vue +0 -91
- package/src/components/PrinterList/PrintersView.vue +0 -492
- package/src/components/Settings/AccountSettings.vue +0 -163
- package/src/components/Settings/DiagnosticsSettings.vue +0 -137
- package/src/components/Settings/EmergencyCommands.vue +0 -265
- package/src/components/Settings/FloorSettings.vue +0 -276
- package/src/components/Settings/GridSettings.vue +0 -127
- package/src/components/Settings/OctoPrintSettings.vue +0 -188
- package/src/components/Settings/ServerProtectionSettings.vue +0 -370
- package/src/components/Settings/SettingsView.vue +0 -73
- package/src/components/Settings/SoftwareUpgradeSettings.vue +0 -297
- package/src/components/Settings/UserManagementSettings.vue +0 -257
- package/src/components/TopBar.vue +0 -147
- package/src/components.d.ts +0 -70
- package/src/directives/file-upload.directive.ts +0 -117
- package/src/directives/printer-drop-position.directive.ts +0 -92
- package/src/env.d.ts +0 -6
- package/src/main.ts +0 -76
- package/src/models/batch/reprint.dto.ts +0 -79
- package/src/models/batch.model.ts +0 -11
- package/src/models/camera-streams/camera-stream.ts +0 -19
- package/src/models/floors/floor.model.ts +0 -30
- package/src/models/octoprint/connection-options.model.ts +0 -8
- package/src/models/plugins/firmware-updates/prusa-firmware-release.model.ts +0 -57
- package/src/models/print-completions/print-completions.model.ts +0 -49
- package/src/models/printers/crud/create-printer.model.ts +0 -26
- package/src/models/printers/file-upload-commands.model.ts +0 -4
- package/src/models/printers/gcode/gcode-analysis.model.ts +0 -30
- package/src/models/printers/printer-current-job.model.ts +0 -90
- package/src/models/printers/printer-file.model.ts +0 -48
- package/src/models/printers/printer.model.ts +0 -18
- package/src/models/server/client-releases.model.ts +0 -27
- package/src/models/server/export-yaml.model.ts +0 -11
- package/src/models/server/features.model.ts +0 -37
- package/src/models/server/github-rate-limit.model.ts +0 -21
- package/src/models/server/version.model.ts +0 -14
- package/src/models/settings/printer-file-clean-settings.model.ts +0 -5
- package/src/models/settings/server-settings.dto.ts +0 -19
- package/src/models/settings/settings.model.ts +0 -57
- package/src/models/socketio-messages/socketio-message.model.ts +0 -53
- package/src/models/uploads/queued-upload.model.ts +0 -12
- package/src/models/user.model.ts +0 -15
- package/src/plugins/README.md +0 -3
- package/src/plugins/index.ts +0 -17
- package/src/plugins/vuetify.ts +0 -53
- package/src/router/index.ts +0 -192
- package/src/router/route-names.ts +0 -14
- package/src/router/utils.ts +0 -23
- package/src/shared/alert.events.ts +0 -14
- package/src/shared/app.constants.ts +0 -23
- package/src/shared/auth.constants.ts +0 -34
- package/src/shared/dialog.composable.ts +0 -41
- package/src/shared/drag.constants.ts +0 -19
- package/src/shared/experimental.constants.ts +0 -1
- package/src/shared/http-client.ts +0 -162
- package/src/shared/noun-adjectives.data.ts +0 -24
- package/src/shared/printer-grid.constants.ts +0 -5
- package/src/shared/printer-state.constants.ts +0 -194
- package/src/shared/snackbar.composable.ts +0 -66
- package/src/shared/socketio.service.ts +0 -104
- package/src/store/auth.store.ts +0 -255
- package/src/store/connection.store.ts +0 -66
- package/src/store/dialog.store.ts +0 -114
- package/src/store/features.store.ts +0 -57
- package/src/store/floor.store.ts +0 -173
- package/src/store/grid.store.ts +0 -10
- package/src/store/index.ts +0 -4
- package/src/store/printer-state.store.ts +0 -246
- package/src/store/printer.store.ts +0 -236
- package/src/store/profile.store.ts +0 -25
- package/src/store/settings.store.ts +0 -64
- package/src/store/test-printer.store.ts +0 -70
- package/src/store/uploads.store.ts +0 -75
- package/src/styles/README.md +0 -3
- package/src/styles/settings.scss +0 -10
- package/src/types/global.d.ts +0 -15
- package/src/utils/array.utils.ts +0 -15
- package/src/utils/date.utils.ts +0 -5
- package/src/utils/download-file.util.ts +0 -25
- package/src/utils/error.utils.ts +0 -3
- package/src/utils/file-size.util.ts +0 -11
- package/src/utils/id.type.ts +0 -1
- package/src/utils/sentry.util.ts +0 -8
- package/src/utils/test.util.ts +0 -30
- package/src/utils/time.utils.ts +0 -2
- package/src/utils/uploads-state.utils.ts +0 -58
- package/src/utils/validation.utils.ts +0 -14
- package/src/vite-env.d.ts +0 -7
- package/test/setup-axios-mock.ts +0 -15
- /package/{src/assets/logo.png → dist/assets/logo-CJVdjy51.png} +0 -0
- /package/{src/assets → dist/img/icons}/android-chrome-192x192.png +0 -0
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-card
|
|
3
|
-
class="pa-4"
|
|
4
|
-
elevation="10"
|
|
5
|
-
style="border-radius: 10px"
|
|
6
|
-
>
|
|
7
|
-
<v-card-text>
|
|
8
|
-
<v-form>
|
|
9
|
-
<label> Username </label>
|
|
10
|
-
<v-text-field
|
|
11
|
-
v-model="username"
|
|
12
|
-
autofocus
|
|
13
|
-
name="login"
|
|
14
|
-
placeholder="Username"
|
|
15
|
-
prepend-icon="mdi-account"
|
|
16
|
-
type="text"
|
|
17
|
-
variant="underlined"
|
|
18
|
-
@keyup.enter="formIsDisabled || login()"
|
|
19
|
-
/>
|
|
20
|
-
<v-text-field
|
|
21
|
-
id="password"
|
|
22
|
-
v-model="password"
|
|
23
|
-
:append-icon="showPassword ? 'visibility_off' : 'visibility'"
|
|
24
|
-
:type="showPassword ? 'text' : 'password'"
|
|
25
|
-
label="Password"
|
|
26
|
-
name="password"
|
|
27
|
-
password
|
|
28
|
-
prepend-icon="lock"
|
|
29
|
-
variant="underlined"
|
|
30
|
-
@click:append="showPassword = !showPassword"
|
|
31
|
-
@keyup.enter="formIsDisabled || login()"
|
|
32
|
-
/>
|
|
33
|
-
<v-alert
|
|
34
|
-
v-if="errorMessage"
|
|
35
|
-
class="mt-6"
|
|
36
|
-
color="error"
|
|
37
|
-
density="compact"
|
|
38
|
-
variant="outlined"
|
|
39
|
-
>
|
|
40
|
-
{{ errorMessage }}
|
|
41
|
-
</v-alert>
|
|
42
|
-
<v-alert
|
|
43
|
-
v-if="authStore.lastLogoutReason"
|
|
44
|
-
class="mt-6"
|
|
45
|
-
color="error-darken-1"
|
|
46
|
-
density="compact"
|
|
47
|
-
variant="outlined"
|
|
48
|
-
>
|
|
49
|
-
Reason for automatic logout: {{ authStore.lastLogoutReason }}
|
|
50
|
-
</v-alert>
|
|
51
|
-
</v-form>
|
|
52
|
-
</v-card-text>
|
|
53
|
-
<v-card-actions>
|
|
54
|
-
<v-btn
|
|
55
|
-
:loading="loading"
|
|
56
|
-
class="pa-4"
|
|
57
|
-
color="primary"
|
|
58
|
-
size="lg"
|
|
59
|
-
variant="flat"
|
|
60
|
-
style="width: 100%"
|
|
61
|
-
@click="login()"
|
|
62
|
-
>
|
|
63
|
-
Login
|
|
64
|
-
</v-btn>
|
|
65
|
-
</v-card-actions>
|
|
66
|
-
<v-card-actions>
|
|
67
|
-
<v-btn
|
|
68
|
-
:disabled="!authStore.registration"
|
|
69
|
-
class="pa-4"
|
|
70
|
-
size="lg"
|
|
71
|
-
variant="flat"
|
|
72
|
-
style="width: 100%"
|
|
73
|
-
@click="gotoRegistration()"
|
|
74
|
-
>
|
|
75
|
-
Register new account {{ authStore.registration ? '' : '(not enabled)' }}
|
|
76
|
-
<v-icon
|
|
77
|
-
class="pl-5"
|
|
78
|
-
icon="right"
|
|
79
|
-
/>
|
|
80
|
-
</v-btn>
|
|
81
|
-
</v-card-actions>
|
|
82
|
-
</v-card>
|
|
83
|
-
</template>
|
|
84
|
-
|
|
85
|
-
<script lang="ts" setup>
|
|
86
|
-
import { computed, onMounted, ref } from 'vue'
|
|
87
|
-
import { useRoute, useRouter } from 'vue-router'
|
|
88
|
-
import { useEventBus } from '@vueuse/core'
|
|
89
|
-
import { AxiosError } from 'axios'
|
|
90
|
-
import { useAuthStore } from '@/store/auth.store'
|
|
91
|
-
import { useSnackbar } from '@/shared/snackbar.composable'
|
|
92
|
-
import { RouteNames } from '@/router/route-names'
|
|
93
|
-
import {
|
|
94
|
-
AUTH_ERROR_REASON,
|
|
95
|
-
convertAuthErrorReason
|
|
96
|
-
} from '@/shared/auth.constants'
|
|
97
|
-
|
|
98
|
-
const authStore = useAuthStore()
|
|
99
|
-
const errorMessage = ref('')
|
|
100
|
-
const username = ref('')
|
|
101
|
-
const showPassword = ref(false)
|
|
102
|
-
const password = ref('')
|
|
103
|
-
const router = useRouter()
|
|
104
|
-
const route = useRoute()
|
|
105
|
-
const loading = ref(false)
|
|
106
|
-
const loginEvent = useEventBus('auth:login')
|
|
107
|
-
const snackbar = useSnackbar()
|
|
108
|
-
|
|
109
|
-
const formIsDisabled = computed(() => {
|
|
110
|
-
return (username.value ?? '')?.length < 3 || (password.value ?? '').length < 3
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
onMounted(async () => {
|
|
114
|
-
authStore.loadTokens()
|
|
115
|
-
await authStore.checkAuthenticationRequirements()
|
|
116
|
-
if (authStore.loginRequired === false) {
|
|
117
|
-
// As AppLoader might not trigger, we trigger it ourselves
|
|
118
|
-
console.debug(
|
|
119
|
-
'LoginView, no login required, redirecting to',
|
|
120
|
-
route.query.redirect,
|
|
121
|
-
'or home'
|
|
122
|
-
)
|
|
123
|
-
loginEvent.emit(true)
|
|
124
|
-
return await routeToRedirect()
|
|
125
|
-
}
|
|
126
|
-
if (!authStore.hasRefreshToken) {
|
|
127
|
-
return
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Check if login is already valid, if so route away safely
|
|
131
|
-
const success = await authStore.verifyOrRefreshLoginOnceOrLogout()
|
|
132
|
-
if (success) {
|
|
133
|
-
await routeToRedirect()
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
async function gotoRegistration() {
|
|
138
|
-
return await router.push({ name: RouteNames.Registration })
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async function login() {
|
|
142
|
-
try {
|
|
143
|
-
loading.value = true
|
|
144
|
-
await authStore.login(username.value, password.value)
|
|
145
|
-
authStore.lastLogoutReason = null
|
|
146
|
-
password.value = ''
|
|
147
|
-
loading.value = false
|
|
148
|
-
} catch (e) {
|
|
149
|
-
loading.value = false
|
|
150
|
-
if ((e as AxiosError)?.response?.status === 401) {
|
|
151
|
-
password.value = ''
|
|
152
|
-
|
|
153
|
-
const reasonCode: keyof typeof AUTH_ERROR_REASON = (
|
|
154
|
-
(e as AxiosError)?.response?.data as any
|
|
155
|
-
)?.reasonCode
|
|
156
|
-
const convertedReason = convertAuthErrorReason(reasonCode)
|
|
157
|
-
if (reasonCode === AUTH_ERROR_REASON.AccountNotVerified) {
|
|
158
|
-
snackbar.error(
|
|
159
|
-
convertedReason,
|
|
160
|
-
'Please ask your administrator to verify your account and try again.'
|
|
161
|
-
)
|
|
162
|
-
} else if (reasonCode === AUTH_ERROR_REASON.PasswordChangeRequired) {
|
|
163
|
-
snackbar.error(
|
|
164
|
-
convertedReason,
|
|
165
|
-
'Your password needs to be changed. This feature is sadly not finished'
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
errorMessage.value = convertedReason
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
snackbar.openErrorMessage({
|
|
174
|
-
title: 'Error logging in',
|
|
175
|
-
subtitle: 'Please test your connection and try again.'
|
|
176
|
-
})
|
|
177
|
-
errorMessage.value =
|
|
178
|
-
'Error logging in - status code ' + (e as AxiosError)?.response?.status
|
|
179
|
-
password.value = ''
|
|
180
|
-
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
errorMessage.value = ''
|
|
185
|
-
|
|
186
|
-
// Trigger AppLoader
|
|
187
|
-
loginEvent.emit(true)
|
|
188
|
-
|
|
189
|
-
return await routeToRedirect()
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
async function routeToRedirect() {
|
|
193
|
-
const routePath = route.query.redirect
|
|
194
|
-
if (!routePath) {
|
|
195
|
-
console.debug('[LoginForm] Redirecting to home')
|
|
196
|
-
await router.push({ name: RouteNames.Home })
|
|
197
|
-
return
|
|
198
|
-
} else {
|
|
199
|
-
console.debug('[LoginForm] Redirecting to ', routePath)
|
|
200
|
-
await router.push({
|
|
201
|
-
path: routePath as string
|
|
202
|
-
})
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
</script>
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils'
|
|
2
|
-
import { describe, it, expect, beforeEach } from 'vitest'
|
|
3
|
-
import LoginView from '@/components/Login/LoginView.vue' // Adjust the path as necessary
|
|
4
|
-
import LoginForm from '@/components/Login/LoginForm.vue'
|
|
5
|
-
import Logo from '@/components/Login/Logo.vue'
|
|
6
|
-
import { createPinia } from 'pinia'
|
|
7
|
-
import { createVuetify } from 'vuetify'
|
|
8
|
-
import { createRouter, createWebHistory } from 'vue-router'
|
|
9
|
-
|
|
10
|
-
const pinia = createPinia()
|
|
11
|
-
const vuetify = createVuetify()
|
|
12
|
-
// Create a router for testing
|
|
13
|
-
const routes = [{ path: '/', component: LoginView }]
|
|
14
|
-
const router = createRouter({
|
|
15
|
-
history: createWebHistory(),
|
|
16
|
-
routes
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
describe('LoginView.vue', () => {
|
|
20
|
-
let wrapper: ReturnType<typeof mount>
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
wrapper = mount(LoginView, {
|
|
24
|
-
global: {
|
|
25
|
-
plugins: [vuetify, pinia, router]
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('renders the logo component', () => {
|
|
31
|
-
const logo = wrapper.findComponent(Logo)
|
|
32
|
-
expect(logo.exists()).toBe(true)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('renders the LoginForm component', () => {
|
|
36
|
-
const loginForm = wrapper.findComponent(LoginForm)
|
|
37
|
-
expect(loginForm.exists()).toBe(true)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('displays the correct title', () => {
|
|
41
|
-
console.log(wrapper.html()) // Log the rendered output for debugging
|
|
42
|
-
|
|
43
|
-
const title = wrapper.find('.text-uppercase.text-red') // Using the class selector
|
|
44
|
-
expect(title.exists()).toBe(true) // Check if the title exists
|
|
45
|
-
expect(title.text()).toContain('FDM')
|
|
46
|
-
expect(title.text()).toContain('Monster')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('displays the correct subtitle', () => {
|
|
50
|
-
const subtitle = wrapper.find('.v-card-subtitle').text()
|
|
51
|
-
expect(subtitle).toBe('Welcome back! Please enter your details')
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('renders with the correct structure', () => {
|
|
55
|
-
expect(wrapper.element).toMatchSnapshot()
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('has the correct CSS class for the login card', () => {
|
|
59
|
-
const loginCard = wrapper.find('.login-card')
|
|
60
|
-
expect(loginCard.exists()).toBe(true)
|
|
61
|
-
expect(loginCard.classes()).toContain('login-card')
|
|
62
|
-
expect(loginCard.attributes('style')).toContain('border-radius: 10px')
|
|
63
|
-
})
|
|
64
|
-
})
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-row
|
|
3
|
-
class="position-relative"
|
|
4
|
-
no-gutters
|
|
5
|
-
>
|
|
6
|
-
<v-col
|
|
7
|
-
cols="12"
|
|
8
|
-
lg="12"
|
|
9
|
-
>
|
|
10
|
-
<v-container>
|
|
11
|
-
<div
|
|
12
|
-
class="d-flex align-center"
|
|
13
|
-
style="min-height: calc(100vh - 148px)"
|
|
14
|
-
>
|
|
15
|
-
<v-row
|
|
16
|
-
justify="center"
|
|
17
|
-
class="align-content-center"
|
|
18
|
-
>
|
|
19
|
-
<div class="pt-6 pl-6 d-flex flex-column align-center">
|
|
20
|
-
<Logo />
|
|
21
|
-
|
|
22
|
-
<v-toolbar-title class="text-uppercase text-red">
|
|
23
|
-
<strong> FDM </strong>
|
|
24
|
-
<strong> Monster </strong>
|
|
25
|
-
</v-toolbar-title>
|
|
26
|
-
|
|
27
|
-
<v-toolbar-title class="mt-lg-6 mt-sm-5 mb-md-5">
|
|
28
|
-
Login to your account
|
|
29
|
-
</v-toolbar-title>
|
|
30
|
-
|
|
31
|
-
<v-card-subtitle class="text-grey mb-md-5">
|
|
32
|
-
Welcome back! Please enter your details
|
|
33
|
-
</v-card-subtitle>
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
<v-col
|
|
37
|
-
cols="12"
|
|
38
|
-
md="12"
|
|
39
|
-
>
|
|
40
|
-
<LoginForm class="login-card" />
|
|
41
|
-
</v-col>
|
|
42
|
-
</v-row>
|
|
43
|
-
</div>
|
|
44
|
-
</v-container>
|
|
45
|
-
</v-col>
|
|
46
|
-
</v-row>
|
|
47
|
-
</template>
|
|
48
|
-
|
|
49
|
-
<script lang="ts" setup>
|
|
50
|
-
import LoginForm from './LoginForm.vue'
|
|
51
|
-
</script>
|
|
52
|
-
|
|
53
|
-
<style lang="scss">
|
|
54
|
-
.login-card {
|
|
55
|
-
max-width: 475px;
|
|
56
|
-
margin: 0 auto;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.blur-logo {
|
|
60
|
-
position: absolute;
|
|
61
|
-
filter: blur(18px);
|
|
62
|
-
bottom: 0;
|
|
63
|
-
transform: inherit;
|
|
64
|
-
}
|
|
65
|
-
</style>
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-container
|
|
3
|
-
fill-height
|
|
4
|
-
fluid
|
|
5
|
-
>
|
|
6
|
-
<v-app-bar color="primary">
|
|
7
|
-
<v-toolbar-title class="text-uppercase text-white">
|
|
8
|
-
<span class="font-weight-light"> FDM </span>
|
|
9
|
-
<strong> Monster </strong>
|
|
10
|
-
</v-toolbar-title>
|
|
11
|
-
</v-app-bar>
|
|
12
|
-
|
|
13
|
-
<img
|
|
14
|
-
alt="FDM Monster Background"
|
|
15
|
-
class="grid-bg-img align-content-center"
|
|
16
|
-
src="/img/logo.svg"
|
|
17
|
-
style="opacity: 0.06"
|
|
18
|
-
/>
|
|
19
|
-
|
|
20
|
-
<v-layout
|
|
21
|
-
align-center
|
|
22
|
-
column
|
|
23
|
-
justify-center
|
|
24
|
-
>
|
|
25
|
-
<v-card
|
|
26
|
-
class="flex align-center justify-center pa-lg-16 pa-sm-10"
|
|
27
|
-
style="width: 80%"
|
|
28
|
-
>
|
|
29
|
-
<v-card-title>
|
|
30
|
-
<v-icon
|
|
31
|
-
class="mr-4"
|
|
32
|
-
size="50"
|
|
33
|
-
>
|
|
34
|
-
lock
|
|
35
|
-
</v-icon>
|
|
36
|
-
<h3>Permission Denied</h3>
|
|
37
|
-
</v-card-title>
|
|
38
|
-
<v-card-subtitle class="mt-1">
|
|
39
|
-
You do not have permission to access a specific resource on this page.
|
|
40
|
-
</v-card-subtitle>
|
|
41
|
-
|
|
42
|
-
<v-card-text v-if="permissionProblems">
|
|
43
|
-
<div v-if="permissionProblems.page">
|
|
44
|
-
Page:
|
|
45
|
-
<strong>
|
|
46
|
-
{{ permissionProblems?.page }}
|
|
47
|
-
</strong>
|
|
48
|
-
</div>
|
|
49
|
-
<div v-if="permissionProblems.permissions">
|
|
50
|
-
Required permission(s):
|
|
51
|
-
<strong
|
|
52
|
-
v-for="perm of permissionProblems.permissions"
|
|
53
|
-
:key="perm"
|
|
54
|
-
>
|
|
55
|
-
{{ perm }}
|
|
56
|
-
</strong>
|
|
57
|
-
</div>
|
|
58
|
-
<div v-if="permissionProblems.roles">
|
|
59
|
-
Usable roles:
|
|
60
|
-
<strong>
|
|
61
|
-
{{ permissionProblems.roles?.join(', ') }}
|
|
62
|
-
</strong>
|
|
63
|
-
</div>
|
|
64
|
-
<div v-if="permissionProblems.url">
|
|
65
|
-
Problematic API path:
|
|
66
|
-
<strong>
|
|
67
|
-
{{ permissionProblems.url }}
|
|
68
|
-
</strong>
|
|
69
|
-
</div>
|
|
70
|
-
<div v-if="permissionProblems.error">
|
|
71
|
-
Error:
|
|
72
|
-
<strong>
|
|
73
|
-
{{ permissionProblems.error }}
|
|
74
|
-
</strong>
|
|
75
|
-
</div>
|
|
76
|
-
</v-card-text>
|
|
77
|
-
<v-card-actions class="mt-6">
|
|
78
|
-
<v-btn
|
|
79
|
-
class="align-center"
|
|
80
|
-
color="primary"
|
|
81
|
-
to="/"
|
|
82
|
-
variant="elevated"
|
|
83
|
-
>
|
|
84
|
-
<v-icon class="mr-2">home</v-icon>
|
|
85
|
-
Go home
|
|
86
|
-
</v-btn>
|
|
87
|
-
</v-card-actions>
|
|
88
|
-
</v-card>
|
|
89
|
-
</v-layout>
|
|
90
|
-
</v-container>
|
|
91
|
-
</template>
|
|
92
|
-
<script lang="ts" setup>
|
|
93
|
-
import { computed } from 'vue'
|
|
94
|
-
import { useRoute } from 'vue-router'
|
|
95
|
-
|
|
96
|
-
const route = useRoute()
|
|
97
|
-
|
|
98
|
-
interface PermissionDeniedQuery {
|
|
99
|
-
roles: string[]
|
|
100
|
-
permissions?: string[]
|
|
101
|
-
url?: string
|
|
102
|
-
page?: string
|
|
103
|
-
error?: string
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const permissionProblems = computed(() => {
|
|
107
|
-
return route.query as any as PermissionDeniedQuery
|
|
108
|
-
})
|
|
109
|
-
</script>
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-container
|
|
3
|
-
fill-height
|
|
4
|
-
fluid
|
|
5
|
-
>
|
|
6
|
-
<v-layout
|
|
7
|
-
align-content-center
|
|
8
|
-
justify-center
|
|
9
|
-
>
|
|
10
|
-
<v-flex
|
|
11
|
-
class="d-flex flex-column align-content-center"
|
|
12
|
-
md4
|
|
13
|
-
sm8
|
|
14
|
-
style="max-width: 450px; margin-top: 10%"
|
|
15
|
-
xs12
|
|
16
|
-
>
|
|
17
|
-
<div class="d-flex flex-column align-center">
|
|
18
|
-
<v-img
|
|
19
|
-
:src="require('@/assets/logo.png')"
|
|
20
|
-
class="shrink mr-1 pt-3 ml-1"
|
|
21
|
-
contain
|
|
22
|
-
style="opacity: 0.85"
|
|
23
|
-
width="150"
|
|
24
|
-
/>
|
|
25
|
-
|
|
26
|
-
<v-toolbar-title class="text-uppercase text-red">
|
|
27
|
-
<strong> FDM </strong>
|
|
28
|
-
<strong> Monster </strong>
|
|
29
|
-
</v-toolbar-title>
|
|
30
|
-
|
|
31
|
-
<v-toolbar-title class="mt-lg-6 mt-sm-5">
|
|
32
|
-
Register new account
|
|
33
|
-
</v-toolbar-title>
|
|
34
|
-
<v-card-subtitle class="text-grey">
|
|
35
|
-
Welcome! Please register your guest account
|
|
36
|
-
</v-card-subtitle>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<v-card
|
|
40
|
-
class="elevation-4 pa-4"
|
|
41
|
-
style="border-radius: 10px"
|
|
42
|
-
>
|
|
43
|
-
<v-card-text>
|
|
44
|
-
<v-form>
|
|
45
|
-
<label> Username </label>
|
|
46
|
-
<v-text-field
|
|
47
|
-
v-model="username"
|
|
48
|
-
:rules="[
|
|
49
|
-
(v) => !!v || 'Username is required',
|
|
50
|
-
(v) =>
|
|
51
|
-
(v?.length ?? 0) >= 3 ||
|
|
52
|
-
'Username must be of length 3 or greater',
|
|
53
|
-
(v) =>
|
|
54
|
-
!v.toLowerCase().includes('admin') ||
|
|
55
|
-
'Username may not contain the word admin',
|
|
56
|
-
(v) =>
|
|
57
|
-
!v.toLowerCase().includes('root') ||
|
|
58
|
-
'Username may not contain the word root',
|
|
59
|
-
(v) =>
|
|
60
|
-
!(v.toLowerCase() === 'demo') ||
|
|
61
|
-
'Username may not equal the word demo'
|
|
62
|
-
]"
|
|
63
|
-
autofocus
|
|
64
|
-
name="login"
|
|
65
|
-
label="Username"
|
|
66
|
-
prepend-icon="person"
|
|
67
|
-
type="text"
|
|
68
|
-
/>
|
|
69
|
-
<v-text-field
|
|
70
|
-
id="password"
|
|
71
|
-
v-model="password"
|
|
72
|
-
:append-icon="showPassword ? 'visibility' : 'visibility_off'"
|
|
73
|
-
:type="showPassword ? 'text' : 'password'"
|
|
74
|
-
label="Password"
|
|
75
|
-
name="password"
|
|
76
|
-
:rules="[
|
|
77
|
-
(v) => !!v || 'Password is required',
|
|
78
|
-
(v) =>
|
|
79
|
-
(!!v && v?.length >= 8) ||
|
|
80
|
-
'Password must be of length 8 or greater'
|
|
81
|
-
]"
|
|
82
|
-
password
|
|
83
|
-
prepend-icon="lock"
|
|
84
|
-
@click:append="showPassword = !showPassword"
|
|
85
|
-
/>
|
|
86
|
-
<v-text-field
|
|
87
|
-
id="password"
|
|
88
|
-
v-model="password2"
|
|
89
|
-
:append-icon="showPassword2 ? 'visibility' : 'visibility_off'"
|
|
90
|
-
:type="showPassword2 ? 'text' : 'password'"
|
|
91
|
-
:rules="[
|
|
92
|
-
(v) => !!v || 'Repeated password is required',
|
|
93
|
-
(v) => v === password || 'Passwords are not equal'
|
|
94
|
-
]"
|
|
95
|
-
label="Repeated Password"
|
|
96
|
-
name="password"
|
|
97
|
-
password
|
|
98
|
-
prepend-icon="lock"
|
|
99
|
-
@click:append="showPassword2 = !showPassword2"
|
|
100
|
-
@keyup.enter="formIsDisabled || registerAccount()"
|
|
101
|
-
/>
|
|
102
|
-
<v-alert
|
|
103
|
-
v-if="errorMessage"
|
|
104
|
-
class="mt-6"
|
|
105
|
-
color="error"
|
|
106
|
-
density="compact"
|
|
107
|
-
variant="outlined"
|
|
108
|
-
>
|
|
109
|
-
{{ errorMessage }}
|
|
110
|
-
</v-alert>
|
|
111
|
-
</v-form>
|
|
112
|
-
</v-card-text>
|
|
113
|
-
<v-card-actions>
|
|
114
|
-
<v-btn
|
|
115
|
-
:loading="loading"
|
|
116
|
-
class="pa-4"
|
|
117
|
-
color="primary"
|
|
118
|
-
size="large"
|
|
119
|
-
style="width: 100%"
|
|
120
|
-
@click="registerAccount()"
|
|
121
|
-
>
|
|
122
|
-
Register account
|
|
123
|
-
</v-btn>
|
|
124
|
-
</v-card-actions>
|
|
125
|
-
<v-card-actions>
|
|
126
|
-
<v-btn
|
|
127
|
-
style="width: 100%"
|
|
128
|
-
class="pa-4"
|
|
129
|
-
size="large"
|
|
130
|
-
@click="gotoLogin()"
|
|
131
|
-
>
|
|
132
|
-
<v-icon class="mr-2">arrow_left</v-icon>Back to Login
|
|
133
|
-
</v-btn>
|
|
134
|
-
</v-card-actions>
|
|
135
|
-
</v-card>
|
|
136
|
-
</v-flex>
|
|
137
|
-
</v-layout>
|
|
138
|
-
</v-container>
|
|
139
|
-
</template>
|
|
140
|
-
<script lang="ts" setup>
|
|
141
|
-
import { AxiosError } from 'axios'
|
|
142
|
-
import { computed, onMounted, ref } from 'vue'
|
|
143
|
-
import { useAuthStore } from '@/store/auth.store'
|
|
144
|
-
import { useRouter } from 'vue-router'
|
|
145
|
-
import { useSnackbar } from '@/shared/snackbar.composable'
|
|
146
|
-
import { AuthService } from '@/backend/auth.service'
|
|
147
|
-
import { RouteNames } from '@/router/route-names'
|
|
148
|
-
|
|
149
|
-
const authStore = useAuthStore()
|
|
150
|
-
const router = useRouter()
|
|
151
|
-
const errorMessage = ref('')
|
|
152
|
-
const username = ref('')
|
|
153
|
-
const showPassword = ref(false)
|
|
154
|
-
const showPassword2 = ref(false)
|
|
155
|
-
const password = ref('')
|
|
156
|
-
const password2 = ref('')
|
|
157
|
-
const loading = ref(false)
|
|
158
|
-
const snackbar = useSnackbar()
|
|
159
|
-
|
|
160
|
-
async function gotoLogin() {
|
|
161
|
-
return await router.push({ name: RouteNames.Login })
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const formIsDisabled = computed(() => {
|
|
165
|
-
return (
|
|
166
|
-
(username.value ?? '')?.length < 3 ||
|
|
167
|
-
(password.value ?? '').length < 3 ||
|
|
168
|
-
password.value != password2.value
|
|
169
|
-
)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
onMounted(async () => {
|
|
173
|
-
await authStore.logout()
|
|
174
|
-
await authStore.checkAuthenticationRequirements()
|
|
175
|
-
if (!authStore.registration) {
|
|
176
|
-
snackbar.info('Registration is disabled, please contact your administrator')
|
|
177
|
-
await router.push({
|
|
178
|
-
name: RouteNames.Login
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
async function registerAccount() {
|
|
184
|
-
try {
|
|
185
|
-
loading.value = true
|
|
186
|
-
await AuthService.registerAccount(username.value, password.value)
|
|
187
|
-
loading.value = false
|
|
188
|
-
} catch (e) {
|
|
189
|
-
loading.value = false
|
|
190
|
-
if ((e as AxiosError)?.response?.status === 401) {
|
|
191
|
-
errorMessage.value = 'Invalid credentials'
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
snackbar.openErrorMessage({
|
|
196
|
-
title: 'Error logging in',
|
|
197
|
-
subtitle: 'Please test your connection and try again.'
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
return
|
|
201
|
-
}
|
|
202
|
-
errorMessage.value = ''
|
|
203
|
-
|
|
204
|
-
snackbar.info('Account created, please login')
|
|
205
|
-
await gotoLogin()
|
|
206
|
-
}
|
|
207
|
-
</script>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-container
|
|
3
|
-
fill-height
|
|
4
|
-
fluid
|
|
5
|
-
>
|
|
6
|
-
<v-layout
|
|
7
|
-
align-center
|
|
8
|
-
justify-center
|
|
9
|
-
>
|
|
10
|
-
<RegistrationForm />
|
|
11
|
-
</v-layout>
|
|
12
|
-
</v-container>
|
|
13
|
-
</template>
|
|
14
|
-
|
|
15
|
-
<script lang="ts" setup>
|
|
16
|
-
import RegistrationForm from '@/components/Login/RegistrationForm.vue'
|
|
17
|
-
</script>
|