@hostlink/nuxt-light 1.37.1 → 1.39.0
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/dist/module.json +1 -1
- package/dist/module.mjs +5 -0
- package/dist/runtime/components/l-app-main.vue +1 -0
- package/dist/runtime/components/l-editor.vue +4 -4
- package/dist/runtime/components/l-input.vue +1 -0
- package/dist/runtime/components/l-login.vue +1 -1
- package/dist/runtime/composables/useLight.d.ts +9 -5
- package/dist/runtime/composables/useLight.js +4 -0
- package/dist/runtime/composables/useRoles.d.ts +7 -0
- package/dist/runtime/composables/useRoles.js +39 -0
- package/dist/runtime/composables/useWebAuthn.d.ts +5 -0
- package/dist/runtime/composables/useWebAuthn.js +5 -0
- package/dist/runtime/pages/Permission/all.vue +175 -48
- package/dist/runtime/pages/System/database/backup.vue +113 -5
- package/dist/runtime/pages/System/database/restore.vue +207 -0
- package/dist/runtime/pages/System/database/restore.vue.d.ts +2 -0
- package/dist/runtime/pages/System/database/table.vue +2 -0
- package/dist/runtime/pages/User/setting/open_id.vue +160 -70
- package/dist/runtime/pages/User/setting/password.vue +3 -2
- package/dist/runtime/pages/User/setting/two-factor-auth.vue +213 -47
- package/package.json +9 -9
|
@@ -11,81 +11,247 @@ const { my } = await q({
|
|
|
11
11
|
const obj = reactive({
|
|
12
12
|
code: ""
|
|
13
13
|
});
|
|
14
|
+
const currentStep = ref(1);
|
|
15
|
+
const secretVisible = ref(false);
|
|
16
|
+
const loading = ref(false);
|
|
14
17
|
const save = async () => {
|
|
18
|
+
loading.value = true;
|
|
15
19
|
try {
|
|
16
20
|
await m("updateMy2FA", {
|
|
17
21
|
code: obj.code,
|
|
18
22
|
secret: my.my2FA.secret
|
|
19
23
|
});
|
|
20
24
|
$q.notify({
|
|
21
|
-
message: "
|
|
22
|
-
color: "positive"
|
|
25
|
+
message: "\u{1F389} Two-Factor Authentication has been successfully enabled!",
|
|
26
|
+
color: "positive",
|
|
27
|
+
timeout: 5e3,
|
|
28
|
+
actions: [
|
|
29
|
+
{ label: "Dismiss", color: "white" }
|
|
30
|
+
]
|
|
23
31
|
});
|
|
32
|
+
await navigateTo("/User/setting");
|
|
24
33
|
} catch (e) {
|
|
25
34
|
$q.notify({
|
|
26
|
-
message: e.message
|
|
27
|
-
color: "negative"
|
|
35
|
+
message: `\u274C Setup failed: ${e.message}`,
|
|
36
|
+
color: "negative",
|
|
37
|
+
timeout: 5e3,
|
|
38
|
+
actions: [
|
|
39
|
+
{ label: "Retry", color: "white" }
|
|
40
|
+
]
|
|
28
41
|
});
|
|
42
|
+
} finally {
|
|
43
|
+
loading.value = false;
|
|
29
44
|
}
|
|
30
45
|
};
|
|
31
46
|
const show = ref(true);
|
|
32
47
|
if (my.twoFactorEnabled) {
|
|
33
48
|
show.value = false;
|
|
49
|
+
currentStep.value = 3;
|
|
34
50
|
}
|
|
35
|
-
const onCopy = () => {
|
|
36
|
-
|
|
51
|
+
const onCopy = async () => {
|
|
52
|
+
try {
|
|
53
|
+
await navigator.clipboard.writeText(my.my2FA.secret);
|
|
37
54
|
$q.notify({
|
|
38
|
-
message: "Secret copied",
|
|
39
|
-
color: "positive"
|
|
55
|
+
message: "\u2705 Secret key copied to clipboard",
|
|
56
|
+
color: "positive",
|
|
57
|
+
timeout: 2e3,
|
|
58
|
+
icon: "content_copy"
|
|
40
59
|
});
|
|
41
|
-
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
const textArea = document.createElement("textarea");
|
|
62
|
+
textArea.value = my.my2FA.secret;
|
|
63
|
+
document.body.appendChild(textArea);
|
|
64
|
+
textArea.select();
|
|
65
|
+
document.execCommand("copy");
|
|
66
|
+
document.body.removeChild(textArea);
|
|
42
67
|
$q.notify({
|
|
43
|
-
message: "
|
|
44
|
-
color: "
|
|
68
|
+
message: "\u2705 Secret key copied to clipboard",
|
|
69
|
+
color: "positive",
|
|
70
|
+
timeout: 2e3
|
|
45
71
|
});
|
|
46
|
-
}
|
|
72
|
+
}
|
|
47
73
|
};
|
|
48
74
|
</script>
|
|
49
75
|
|
|
76
|
+
<style scoped>
|
|
77
|
+
.two-factor-setup{margin:0 auto;max-width:800px}.setup-header{text-align:center}.app-card{transition:all .3s ease}.app-card:hover{box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.qr-card{background:linear-gradient(135deg,#f5f7fa,#c3cfe2);border:2px dashed #1976d2}.secret-input{font-family:Courier New,monospace;font-size:14px}.code-input .q-field__control input{font-family:Courier New,monospace;font-size:24px;font-weight:700;letter-spacing:8px;text-align:center}.verify-section{text-align:center}@media (max-width:600px){.app-card{margin-bottom:1rem}.qr-card img{width:180px!important}.code-input .q-field__control input{font-size:20px;letter-spacing:4px}}.q-btn--loading{pointer-events:none}.q-stepper--vertical .q-stepper__step--done .q-stepper__label{opacity:.7}.q-btn,.q-card{transition:all .2s ease}.q-btn:hover{transform:translateY(-1px)}
|
|
78
|
+
</style>
|
|
79
|
+
|
|
50
80
|
<template>
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
<div class="two-factor-setup">
|
|
82
|
+
<!-- Header -->
|
|
83
|
+
<div class="setup-header q-mb-lg">
|
|
84
|
+
<h5 class="q-my-md">{{ $t('Setup Two-Factor Authentication') }}</h5>
|
|
85
|
+
<q-stepper v-model="currentStep" vertical color="primary" animated flat>
|
|
86
|
+
<q-step :name="1" title="Download Authenticator App" icon="download" :done="currentStep > 1">
|
|
87
|
+
<div class="q-mb-md">
|
|
88
|
+
<p class="text-body1 q-mb-md">{{ $t('Choose and download an authenticator app for your device:') }}</p>
|
|
89
|
+
|
|
90
|
+
<!-- App Selection Cards -->
|
|
91
|
+
<div class="row q-gutter-md q-mb-lg">
|
|
92
|
+
<div class="col-12 col-md-5">
|
|
93
|
+
<q-card flat bordered class="app-card">
|
|
94
|
+
<q-card-section class="q-pa-md">
|
|
95
|
+
<div class="text-h6 q-mb-sm">📱 Android</div>
|
|
96
|
+
<div class="q-gutter-sm">
|
|
97
|
+
<q-btn
|
|
98
|
+
outline
|
|
99
|
+
color="primary"
|
|
100
|
+
size="sm"
|
|
101
|
+
target="_blank"
|
|
102
|
+
href="https://play.google.com/store/apps/details?id=com.azure.authenticator"
|
|
103
|
+
label="Microsoft Authenticator"
|
|
104
|
+
class="full-width"
|
|
105
|
+
/>
|
|
106
|
+
<q-btn
|
|
107
|
+
outline
|
|
108
|
+
color="primary"
|
|
109
|
+
size="sm"
|
|
110
|
+
target="_blank"
|
|
111
|
+
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"
|
|
112
|
+
label="Google Authenticator"
|
|
113
|
+
class="full-width"
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
</q-card-section>
|
|
117
|
+
</q-card>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div class="col-12 col-md-5">
|
|
121
|
+
<q-card flat bordered class="app-card">
|
|
122
|
+
<q-card-section class="q-pa-md">
|
|
123
|
+
<div class="text-h6 q-mb-sm">🍎 iOS</div>
|
|
124
|
+
<div class="q-gutter-sm">
|
|
125
|
+
<q-btn
|
|
126
|
+
outline
|
|
127
|
+
color="primary"
|
|
128
|
+
size="sm"
|
|
129
|
+
target="_blank"
|
|
130
|
+
href="https://apps.apple.com/us/app/microsoft-authenticator/id983156458"
|
|
131
|
+
label="Microsoft Authenticator"
|
|
132
|
+
class="full-width"
|
|
133
|
+
/>
|
|
134
|
+
<q-btn
|
|
135
|
+
outline
|
|
136
|
+
color="primary"
|
|
137
|
+
size="sm"
|
|
138
|
+
target="_blank"
|
|
139
|
+
href="https://apps.apple.com/us/app/google-authenticator/id388497605"
|
|
140
|
+
label="Google Authenticator"
|
|
141
|
+
class="full-width"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
</q-card-section>
|
|
145
|
+
</q-card>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
61
148
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
149
|
+
<q-btn
|
|
150
|
+
color="primary"
|
|
151
|
+
@click="currentStep = 2"
|
|
152
|
+
:label="$t('I have installed the app')"
|
|
153
|
+
icon-right="arrow_forward"
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
</q-step>
|
|
66
157
|
|
|
67
|
-
|
|
158
|
+
<q-step :name="2" title="Scan QR Code" icon="qr_code" :done="currentStep > 2">
|
|
159
|
+
<div class="text-center q-mb-md">
|
|
160
|
+
<p class="text-body1 q-mb-md">{{ $t('Scan this QR code with your authenticator app:') }}</p>
|
|
161
|
+
|
|
162
|
+
<q-card flat bordered class="qr-card inline-block q-pa-lg">
|
|
163
|
+
<q-img :src="my.my2FA.image" width="200px" class="q-mb-md" />
|
|
164
|
+
|
|
165
|
+
<!-- Manual Entry Option -->
|
|
166
|
+
<q-expansion-item
|
|
167
|
+
icon="key"
|
|
168
|
+
:label="$t('Enter manually instead')"
|
|
169
|
+
class="text-left"
|
|
170
|
+
>
|
|
171
|
+
<div class="q-pa-md bg-grey-1 rounded-borders">
|
|
172
|
+
<div class="text-caption text-grey-7 q-mb-xs">{{ $t('Secret Key:') }}</div>
|
|
173
|
+
<div class="row items-center q-gutter-sm">
|
|
174
|
+
<div class="col">
|
|
175
|
+
<q-input
|
|
176
|
+
:model-value="secretVisible ? (my.my2FA?.secret || '') : '●'.repeat(my.my2FA?.secret?.length || 16)"
|
|
177
|
+
readonly
|
|
178
|
+
dense
|
|
179
|
+
outlined
|
|
180
|
+
class="secret-input"
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
<q-btn
|
|
184
|
+
flat
|
|
185
|
+
round
|
|
186
|
+
dense
|
|
187
|
+
:icon="secretVisible ? 'visibility_off' : 'visibility'"
|
|
188
|
+
@click="secretVisible = !secretVisible"
|
|
189
|
+
:title="secretVisible ? $t('Hide') : $t('Show')"
|
|
190
|
+
/>
|
|
191
|
+
<q-btn
|
|
192
|
+
flat
|
|
193
|
+
round
|
|
194
|
+
dense
|
|
195
|
+
icon="content_copy"
|
|
196
|
+
@click="onCopy"
|
|
197
|
+
:title="$t('Copy to clipboard')"
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</q-expansion-item>
|
|
202
|
+
</q-card>
|
|
68
203
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
204
|
+
<div class="q-mt-md">
|
|
205
|
+
<q-btn
|
|
206
|
+
color="primary"
|
|
207
|
+
@click="currentStep = 3"
|
|
208
|
+
:label="$t('I have scanned the code')"
|
|
209
|
+
icon-right="arrow_forward"
|
|
210
|
+
/>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</q-step>
|
|
78
214
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
215
|
+
<q-step :name="3" title="Verify Setup" icon="verified_user">
|
|
216
|
+
<div class="verify-section">
|
|
217
|
+
<p class="text-body1 q-mb-md">
|
|
218
|
+
{{ $t('Enter the 6-digit code from your authenticator app to complete setup:') }}
|
|
219
|
+
</p>
|
|
220
|
+
|
|
221
|
+
<div class="row justify-center q-mb-lg">
|
|
222
|
+
<div class="col-12 col-sm-8 col-md-6">
|
|
223
|
+
<q-input
|
|
224
|
+
v-model="obj.code"
|
|
225
|
+
:label="$t('Verification Code')"
|
|
226
|
+
hint="6-digit code from your authenticator app"
|
|
227
|
+
type="tel"
|
|
228
|
+
inputmode="numeric"
|
|
229
|
+
pattern="[0-9]*"
|
|
230
|
+
outlined
|
|
231
|
+
maxlength="6"
|
|
232
|
+
class="text-center code-input"
|
|
233
|
+
required
|
|
234
|
+
>
|
|
235
|
+
<template v-slot:prepend>
|
|
236
|
+
<q-icon name="security" />
|
|
237
|
+
</template>
|
|
238
|
+
</q-input>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
87
241
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
242
|
+
<div class="text-center">
|
|
243
|
+
<q-btn
|
|
244
|
+
@click="save"
|
|
245
|
+
color="positive"
|
|
246
|
+
size="lg"
|
|
247
|
+
:label="$t('Complete Setup')"
|
|
248
|
+
icon="check_circle"
|
|
249
|
+
:loading="loading"
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
</q-step>
|
|
254
|
+
</q-stepper>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
91
257
|
</template>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostlink/nuxt-light",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.0",
|
|
4
4
|
"description": "HostLink Nuxt Light Framework",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,31 +32,31 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@azure/msal-browser": "^3.26.1",
|
|
34
34
|
"@formkit/drag-and-drop": "^0.5.3",
|
|
35
|
-
"@hostlink/light": "^2.
|
|
35
|
+
"@hostlink/light": "^2.9.0",
|
|
36
36
|
"@nuxt/module-builder": "^1.0.1",
|
|
37
|
-
"@quasar/extras": "^1.
|
|
37
|
+
"@quasar/extras": "^1.17.0",
|
|
38
38
|
"@quasar/quasar-ui-qmarkdown": "^2.0.5",
|
|
39
|
-
"axios": "^1.
|
|
39
|
+
"axios": "^1.12.2",
|
|
40
40
|
"defu": "^6.1.4",
|
|
41
41
|
"diff2html": "^3.4.47",
|
|
42
42
|
"formkit-quasar": "^0.0.15",
|
|
43
43
|
"json-to-graphql-query": "^2.2.5",
|
|
44
44
|
"nuxt-quasar-ui": "^2.1.12",
|
|
45
|
-
"quasar": "^2.
|
|
45
|
+
"quasar": "^2.18.5",
|
|
46
46
|
"vue-i18n": "^9.2.2",
|
|
47
47
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@nuxt/devtools": "latest",
|
|
51
51
|
"@nuxt/eslint-config": "^0.2.0",
|
|
52
|
-
"@nuxt/kit": "^4.
|
|
53
|
-
"@nuxt/schema": "^4.
|
|
52
|
+
"@nuxt/kit": "^4.1.2",
|
|
53
|
+
"@nuxt/schema": "^4.1.2",
|
|
54
54
|
"@nuxt/test-utils": "^3.17.2",
|
|
55
55
|
"@types/node": "^22.5.0",
|
|
56
56
|
"changelogen": "^0.5.4",
|
|
57
57
|
"eslint": "^8.46.0",
|
|
58
|
-
"nuxt": "^4.
|
|
59
|
-
"typescript": "^5.
|
|
58
|
+
"nuxt": "^4.1.2",
|
|
59
|
+
"typescript": "^5.9.2",
|
|
60
60
|
"vue-tsc": "^2.2.8"
|
|
61
61
|
}
|
|
62
62
|
}
|